commit c24927fab1a36f678e6fd8421f658664b1573d30 Author: DerJesen Date: Wed Dec 10 20:20:39 2025 +0100 first commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..280a639 --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +# Database Connection +DATABASE_URL="postgresql://user:password@host:5432/database?schema=public" + +# Security +JWT_SECRET="change_this_to_a_secure_secret" + +# SMTP Configuration +SMTP_HOST="smtp.example.com" +SMTP_PORT=587 +SMTP_USER="user@example.com" +SMTP_PASS="password" +SMTP_FROM="noreply@smartes-klassenzimmer.de" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..829ebda --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Dependencies +node_modules/ + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Build outputs +dist/ +build/ +out/ + +# Logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +logs +*.log + +# IDEs +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Test coverage +coverage/ + +# NestJS specific +*.tsbuildinfo + +# SvelteKit specific +.svelte-kit/ diff --git a/Smartes-Klassenzimmer-Backend/.env.example b/Smartes-Klassenzimmer-Backend/.env.example new file mode 100644 index 0000000..f5c74ab --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/.env.example @@ -0,0 +1,16 @@ +# PostgreSQL Database Connection +# Format: postgresql://username:password@host:port/database?schema=public +DATABASE_URL="postgresql://username:password@localhost:5432/smartes_klassenzimmer?schema=public" + +# JWT Secret Key (MUST be changed in production!) +JWT_SECRET="your-super-secret-jwt-key-change-this-in-production" + +# Server Port +PORT=3000 + +# SMTP Configuration +SMTP_HOST="smtp.example.com" +SMTP_PORT=587 +SMTP_USER="user@example.com" +SMTP_PASS="password" +SMTP_FROM="noreply@smartes-klassenzimmer.de" \ No newline at end of file diff --git a/Smartes-Klassenzimmer-Backend/.gitignore b/Smartes-Klassenzimmer-Backend/.gitignore new file mode 100644 index 0000000..13e0355 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/.gitignore @@ -0,0 +1,75 @@ +# compiled output +/dist +/node_modules +/build + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output +*.spec.ts + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# temp directory +.temp +.tmp + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Claude +.claude/* + +# Test files +/test +test-websocket.html +*.html + +# Prisma +/generated/prisma + +# Database +*.db +*.db-journal + +# Proton Drive sync conflicts +*# Name clash* diff --git a/Smartes-Klassenzimmer-Backend/.prettierrc b/Smartes-Klassenzimmer-Backend/.prettierrc new file mode 100644 index 0000000..a20502b --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} diff --git a/Smartes-Klassenzimmer-Backend/Dockerfile b/Smartes-Klassenzimmer-Backend/Dockerfile new file mode 100644 index 0000000..6bdcab3 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/Dockerfile @@ -0,0 +1,20 @@ +FROM node:22-alpine + +WORKDIR /app + +# Install OpenSSL for Prisma +RUN apk add --no-cache openssl + +COPY package*.json ./ +RUN npm ci + +COPY . . + +# Generate Prisma Client +RUN npx prisma generate + +RUN npm run build + +EXPOSE 3000 + +CMD ["sh", "-c", "npx prisma migrate deploy && node dist/src/main.js"] diff --git a/Smartes-Klassenzimmer-Backend/README.md b/Smartes-Klassenzimmer-Backend/README.md new file mode 100644 index 0000000..920991d --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/README.md @@ -0,0 +1,19 @@ +# Smartes Klassenzimmer - Backend + +Vollständige Projekt-Dokumentation befindet sich in der [CLAUDE.md](../CLAUDE.md) im Root-Verzeichnis. + +## Quick Start + +```bash +# Dependencies installieren +npm install + +# Datenbank initialisieren +npx prisma migrate dev +npx prisma db seed + +# Development Server starten +npm run start:dev +``` + +Der Server läuft auf `http://localhost:3000` mit API-Präfix `/api`. diff --git a/Smartes-Klassenzimmer-Backend/eslint.config.mjs b/Smartes-Klassenzimmer-Backend/eslint.config.mjs new file mode 100644 index 0000000..4e9f827 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/eslint.config.mjs @@ -0,0 +1,35 @@ +// @ts-check +import eslint from '@eslint/js'; +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + ignores: ['eslint.config.mjs'], + }, + eslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + eslintPluginPrettierRecommended, + { + languageOptions: { + globals: { + ...globals.node, + ...globals.jest, + }, + sourceType: 'commonjs', + parserOptions: { + projectService: true, + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + rules: { + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-floating-promises': 'warn', + '@typescript-eslint/no-unsafe-argument': 'warn', + "prettier/prettier": ["error", { endOfLine: "auto" }], + }, + }, +); diff --git a/Smartes-Klassenzimmer-Backend/nest-cli.json b/Smartes-Klassenzimmer-Backend/nest-cli.json new file mode 100644 index 0000000..8de560e --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/nest-cli.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "sourceRoot": "src", + "compilerOptions": { + "deleteOutDir": true, + "assets": ["mail/templates/**/*"] + } +} diff --git a/Smartes-Klassenzimmer-Backend/package-lock.json b/Smartes-Klassenzimmer-Backend/package-lock.json new file mode 100644 index 0000000..55cc9e9 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/package-lock.json @@ -0,0 +1,12303 @@ +{ + "name": "Smartes-Klassenzimmer-Backend", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "Smartes-Klassenzimmer-Backend", + "version": "0.0.1", + "license": "UNLICENSED", + "dependencies": { + "@nestjs-modules/mailer": "^2.0.2", + "@nestjs/common": "^11.1.8", + "@nestjs/config": "^4.0.2", + "@nestjs/core": "^11.0.1", + "@nestjs/jwt": "^11.0.1", + "@nestjs/mapped-types": "*", + "@nestjs/passport": "^11.0.5", + "@nestjs/platform-express": "^11.1.8", + "@nestjs/platform-socket.io": "^11.1.8", + "@nestjs/schedule": "^6.1.0", + "@nestjs/websockets": "^11.1.8", + "@prisma/client": "^6.19.0", + "@types/cron": "^2.0.1", + "@types/multer": "^2.0.0", + "bcrypt": "^6.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.2", + "cookie-parser": "^1.4.7", + "dotenv": "^17.2.3", + "handlebars": "^4.7.8", + "multer": "^2.0.2", + "nodemailer": "^7.0.11", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "socket.io": "^4.8.1" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.18.0", + "@nestjs/cli": "^11.0.0", + "@nestjs/schematics": "^11.0.0", + "@types/bcrypt": "^6.0.0", + "@types/cookie-parser": "^1.4.10", + "@types/express": "^5.0.0", + "@types/node": "^22.10.7", + "@types/nodemailer": "^7.0.4", + "@types/passport-jwt": "^4.0.1", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2", + "globals": "^16.0.0", + "prettier": "^3.4.2", + "prisma": "^6.19.0", + "source-map-support": "^0.5.21", + "ts-loader": "^9.5.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.7.3", + "typescript-eslint": "^8.20.0" + } + }, + "node_modules/@angular-devkit/core": { + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.15.tgz", + "integrity": "sha512-pU2RZYX6vhd7uLSdLwPnuBcr0mXJSjp3EgOXKsrlQFQZevc+Qs+2JdXgIElnOT/aDqtRtriDmLlSbtdE8n3ZbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/core/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.15.tgz", + "integrity": "sha512-kNOJ+3vekJJCQKWihNmxBkarJzNW09kP5a9E1SRNiQVNOUEeSwcRR0qYotM65nx821gNzjjhJXnAZ8OazWldrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.15", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli": { + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.15.tgz", + "integrity": "sha512-1ESFmFGMpGQmalDB3t2EtmWDGv6gOFYBMxmHO2f1KI/UDl8UmZnCGL4mD3EWo8Hv0YIsZ9wOH9Q7ZHNYjeSpzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.15", + "@angular-devkit/schematics": "19.2.15", + "@inquirer/prompts": "7.3.2", + "ansi-colors": "4.1.3", + "symbol-observable": "4.0.0", + "yargs-parser": "21.1.1" + }, + "bin": { + "schematics": "bin/schematics.js" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", + "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.2", + "@inquirer/confirm": "^5.1.6", + "@inquirer/editor": "^4.2.7", + "@inquirer/expand": "^4.0.9", + "@inquirer/input": "^4.1.6", + "@inquirer/number": "^3.0.9", + "@inquirer/password": "^4.0.9", + "@inquirer/rawlist": "^4.0.9", + "@inquirer/search": "^3.0.9", + "@inquirer/select": "^4.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-sesv2": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.946.0.tgz", + "integrity": "sha512-JYj3BPqgyRXgBjZ3Xvo4Abd+vLxcsHe4gb0TvwiSM/k7e6MRgBZoYwDOnwbNDs/62X1sn7MPHqqB3miuO4nR5g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.946.0", + "@aws-sdk/credential-provider-node": "3.946.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.946.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/signature-v4-multi-region": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.946.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.946.0.tgz", + "integrity": "sha512-kGAs5iIVyUz4p6TX3pzG5q3cNxXnVpC4pwRC6DCSaSv9ozyPjc2d74FsK4fZ+J+ejtvCdJk72uiuQtWJc86Wuw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.946.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.946.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.946.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.946.0.tgz", + "integrity": "sha512-u2BkbLLVbMFrEiXrko2+S6ih5sUZPlbVyRPtXOqMHlCyzr70sE8kIiD6ba223rQeIFPcYfW/wHc6k4ihW2xxVg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws-sdk/xml-builder": "3.930.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.946.0.tgz", + "integrity": "sha512-P4l+K6wX1tf8LmWUvZofdQ+BgCNyk6Tb9u1H10npvqpuCD+dCM4pXIBq3PQcv/juUBOvLGGREo+Govuh3lfD0Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.946.0.tgz", + "integrity": "sha512-/zeOJ6E7dGZQ/l2k7KytEoPJX0APIhwt0A79hPf/bUpMF4dDs2P6JmchDrotk0a0Y/MIdNF8sBQ/MEOPnBiYoQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.946.0.tgz", + "integrity": "sha512-Pdgcra3RivWj/TuZmfFaHbqsvvgnSKO0CxlRUMMr0PgBiCnUhyl+zBktdNOeGsOPH2fUzQpYhcUjYUgVSdcSDQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/credential-provider-env": "3.946.0", + "@aws-sdk/credential-provider-http": "3.946.0", + "@aws-sdk/credential-provider-login": "3.946.0", + "@aws-sdk/credential-provider-process": "3.946.0", + "@aws-sdk/credential-provider-sso": "3.946.0", + "@aws-sdk/credential-provider-web-identity": "3.946.0", + "@aws-sdk/nested-clients": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-login": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.946.0.tgz", + "integrity": "sha512-5iqLNc15u2Zx+7jOdQkIbP62N7n2031tw5hkmIG0DLnozhnk64osOh2CliiOE9x3c4P9Pf4frAwgyy9GzNTk2g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/nested-clients": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.946.0.tgz", + "integrity": "sha512-I7URUqnBPng1a5y81OImxrwERysZqMBREG6svhhGeZgxmqcpAZ8z5ywILeQXdEOCuuES8phUp/ojzxFjPXp/eA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.946.0", + "@aws-sdk/credential-provider-http": "3.946.0", + "@aws-sdk/credential-provider-ini": "3.946.0", + "@aws-sdk/credential-provider-process": "3.946.0", + "@aws-sdk/credential-provider-sso": "3.946.0", + "@aws-sdk/credential-provider-web-identity": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.946.0.tgz", + "integrity": "sha512-GtGHX7OGqIeVQ3DlVm5RRF43Qmf3S1+PLJv9svrdvAhAdy2bUb044FdXXqrtSsIfpzTKlHgQUiRo5MWLd35Ntw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.946.0.tgz", + "integrity": "sha512-LeGSSt2V5iwYey1ENGY75RmoDP3bA2iE/py8QBKW8EDA8hn74XBLkprhrK5iccOvU3UGWY8WrEKFAFGNjJOL9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.946.0", + "@aws-sdk/core": "3.946.0", + "@aws-sdk/token-providers": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.946.0.tgz", + "integrity": "sha512-ocBCvjWfkbjxElBI1QUxOnHldsNhoU0uOICFvuRDAZAoxvypJHN3m5BJkqb7gqorBbcv3LRgmBdEnWXOAvq+7Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/nested-clients": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.936.0.tgz", + "integrity": "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.936.0.tgz", + "integrity": "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.936.0.tgz", + "integrity": "sha512-l4aGbHpXM45YNgXggIux1HgsCVAvvBoqHPkqLnqMl9QVapfuSTjJHfDYDsx1Xxct6/m7qSMUzanBALhiaGO2fA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@aws/lambda-invoke-store": "^0.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.946.0.tgz", + "integrity": "sha512-0UTFmFd8PX2k/jLu/DBmR+mmLQWAtUGHYps9Rjx3dcXNwaMLaa/39NoV3qn7Dwzfpqc6JZlZzBk+NDOCJIHW9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-arn-parser": "3.893.0", + "@smithy/core": "^3.18.7", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.946.0.tgz", + "integrity": "sha512-7QcljCraeaWQNuqmOoAyZs8KpZcuhPiqdeeKoRd397jVGNRehLFsZbIMOvwaluUDFY11oMyXOkQEERe1Zo2fCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@smithy/core": "^3.18.7", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.946.0.tgz", + "integrity": "sha512-rjAtEguukeW8mlyEQMQI56vxFoyWlaNwowmz1p1rav948SUjtrzjHAp4TOQWhibb7AR7BUTHBCgIcyCRjBEf4g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.946.0", + "@aws-sdk/middleware-host-header": "3.936.0", + "@aws-sdk/middleware-logger": "3.936.0", + "@aws-sdk/middleware-recursion-detection": "3.936.0", + "@aws-sdk/middleware-user-agent": "3.946.0", + "@aws-sdk/region-config-resolver": "3.936.0", + "@aws-sdk/types": "3.936.0", + "@aws-sdk/util-endpoints": "3.936.0", + "@aws-sdk/util-user-agent-browser": "3.936.0", + "@aws-sdk/util-user-agent-node": "3.946.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/core": "^3.18.7", + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/hash-node": "^4.2.5", + "@smithy/invalid-dependency": "^4.2.5", + "@smithy/middleware-content-length": "^4.2.5", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-retry": "^4.4.14", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-body-length-node": "^4.2.1", + "@smithy/util-defaults-mode-browser": "^4.3.13", + "@smithy/util-defaults-mode-node": "^4.2.16", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.936.0.tgz", + "integrity": "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/config-resolver": "^4.4.3", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.946.0.tgz", + "integrity": "sha512-61FZ685lKiJuQ06g6U7K3PL9EwKCxNm51wNlxyKV57nnl1GrLD0NC8O3/hDNkCQLNBArT9y3IXl2H7TtIxP8Jg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-sdk-s3": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/signature-v4": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.946.0.tgz", + "integrity": "sha512-a5c+rM6CUPX2ExmUZ3DlbLlS5rQr4tbdoGcgBsjnAHiYx8MuMNAI+8M7wfjF13i2yvUQj5WEIddvLpayfEZj9g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.946.0", + "@aws-sdk/nested-clients": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.936.0.tgz", + "integrity": "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz", + "integrity": "sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.936.0.tgz", + "integrity": "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-endpoints": "^3.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.893.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.893.0.tgz", + "integrity": "sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.936.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.936.0.tgz", + "integrity": "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.936.0", + "@smithy/types": "^4.9.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.946.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.946.0.tgz", + "integrity": "sha512-a2UwwvzbK5AxHKUBupfg4s7VnkqRAHjYsuezHnKCniczmT4HZfP1NnfwwvLKEH8qaTrwenxjKSfq4UWmWkvG+Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.946.0", + "@aws-sdk/types": "3.936.0", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/xml-builder": { + "version": "3.930.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.930.0.tgz", + "integrity": "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "fast-xml-parser": "5.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aws/lambda-invoke-store": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.2.tgz", + "integrity": "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@borewit/text-codec": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", + "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@css-inline/css-inline": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline/-/css-inline-0.14.1.tgz", + "integrity": "sha512-u4eku+hnPqqHIGq/ZUQcaP0TrCbYeLIYBaK7qClNRGZbnh8RC4gVxLEIo8Pceo1nOK9E5G4Lxzlw5KnXcvflfA==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@css-inline/css-inline-android-arm-eabi": "0.14.1", + "@css-inline/css-inline-android-arm64": "0.14.1", + "@css-inline/css-inline-darwin-arm64": "0.14.1", + "@css-inline/css-inline-darwin-x64": "0.14.1", + "@css-inline/css-inline-linux-arm-gnueabihf": "0.14.1", + "@css-inline/css-inline-linux-arm64-gnu": "0.14.1", + "@css-inline/css-inline-linux-arm64-musl": "0.14.1", + "@css-inline/css-inline-linux-x64-gnu": "0.14.1", + "@css-inline/css-inline-linux-x64-musl": "0.14.1", + "@css-inline/css-inline-win32-x64-msvc": "0.14.1" + } + }, + "node_modules/@css-inline/css-inline-android-arm-eabi": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-android-arm-eabi/-/css-inline-android-arm-eabi-0.14.1.tgz", + "integrity": "sha512-LNUR8TY4ldfYi0mi/d4UNuHJ+3o8yLQH9r2Nt6i4qeg1i7xswfL3n/LDLRXvGjBYqeEYNlhlBQzbPwMX1qrU6A==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-android-arm64": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-android-arm64/-/css-inline-android-arm64-0.14.1.tgz", + "integrity": "sha512-tH5us0NYGoTNBHOUHVV7j9KfJ4DtFOeTLA3cM0XNoMtArNu2pmaaBMFJPqECzavfXkLc7x5Z22UPZYjoyHfvCA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-darwin-arm64": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-arm64/-/css-inline-darwin-arm64-0.14.1.tgz", + "integrity": "sha512-QE5W1YRIfRayFrtrcK/wqEaxNaqLULPI0gZB4ArbFRd3d56IycvgBasDTHPre5qL2cXCO3VyPx+80XyHOaVkag==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-darwin-x64": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-darwin-x64/-/css-inline-darwin-x64-0.14.1.tgz", + "integrity": "sha512-mAvv2sN8awNFsbvBzlFkZPbCNZ6GCWY5/YcIz7V5dPYw+bHHRbjnlkNTEZq5BsDxErVrMIGvz05PGgzuNvZvdQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm-gnueabihf": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm-gnueabihf/-/css-inline-linux-arm-gnueabihf-0.14.1.tgz", + "integrity": "sha512-AWC44xL0X7BgKvrWEqfSqkT2tJA5kwSGrAGT+m0gt11wnTYySvQ6YpX0fTY9i3ppYGu4bEdXFjyK2uY1DTQMHA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm64-gnu": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-gnu/-/css-inline-linux-arm64-gnu-0.14.1.tgz", + "integrity": "sha512-drj0ciiJgdP3xKXvNAt4W+FH4KKMs8vB5iKLJ3HcH07sNZj58Sx++2GxFRS1el3p+GFp9OoYA6dgouJsGEqt0Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-arm64-musl": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-arm64-musl/-/css-inline-linux-arm64-musl-0.14.1.tgz", + "integrity": "sha512-FzknI+st8eA8YQSdEJU9ykcM0LZjjigBuynVF5/p7hiMm9OMP8aNhWbhZ8LKJpKbZrQsxSGS4g9Vnr6n6FiSdQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-x64-gnu": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-gnu/-/css-inline-linux-x64-gnu-0.14.1.tgz", + "integrity": "sha512-yubbEye+daDY/4vXnyASAxH88s256pPati1DfVoZpU1V0+KP0BZ1dByZOU1ktExurbPH3gZOWisAnBE9xon0Uw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-linux-x64-musl": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-linux-x64-musl/-/css-inline-linux-x64-musl-0.14.1.tgz", + "integrity": "sha512-6CRAZzoy1dMLPC/tns2rTt1ZwPo0nL/jYBEIAsYTCWhfAnNnpoLKVh5Nm+fSU3OOwTTqU87UkGrFJhObD/wobQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@css-inline/css-inline-win32-x64-msvc": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@css-inline/css-inline-win32-x64-msvc/-/css-inline-win32-x64-msvc-0.14.1.tgz", + "integrity": "sha512-nzotGiaiuiQW78EzsiwsHZXbxEt6DiMUFcDJ6dhiliomXxnlaPyBfZb6/FMBgRJOf6sknDt/5695OttNmbMYzg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@inquirer/ansi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.1.tgz", + "integrity": "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.0.tgz", + "integrity": "sha512-5+Q3PKH35YsnoPTh75LucALdAxom6xh5D1oeY561x4cqBuH24ZFVyFREPe14xgnrtmGu3EEt1dIi60wRVSnGCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.19", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.19.tgz", + "integrity": "sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.0.tgz", + "integrity": "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.1", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.21", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.21.tgz", + "integrity": "sha512-MjtjOGjr0Kh4BciaFShYpZ1s9400idOdvQ5D7u7lE6VztPFoyLcVNE5dXBmEEIQq5zi4B9h2kU+q7AVBxJMAkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/external-editor": "^1.0.2", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.21.tgz", + "integrity": "sha512-+mScLhIcbPFmuvU3tAGBed78XvYHSvCl6dBiYMlzCLhpr0bzGzd8tfivMMeqND6XZiaZ1tgusbUHJEfc6YzOdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.2.tgz", + "integrity": "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.0", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.14.tgz", + "integrity": "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.5.tgz", + "integrity": "sha512-7GoWev7P6s7t0oJbenH0eQ0ThNdDJbEAEtVt9vsrYZ9FulIokvd823yLyhQlWHJPGce1wzP53ttfdCZmonMHyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.21.tgz", + "integrity": "sha512-5QWs0KGaNMlhbdhOSCFfKsW+/dcAVC2g4wT/z2MCiZM47uLgatC5N20kpkDQf7dHx+XFct/MJvvNGy6aYJn4Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.21.tgz", + "integrity": "sha512-xxeW1V5SbNFNig2pLfetsDb0svWlKuhmr7MPJZMYuDnCTkpVBI+X/doudg4pznc1/U+yYmWFFOi4hNvGgUo7EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.0.tgz", + "integrity": "sha512-JHwGbQ6wjf1dxxnalDYpZwZxUEosT+6CPGD9Zh4sm9WXdtUp9XODCQD3NjSTmu+0OAyxWXNOqf0spjIymJa2Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.2.0", + "@inquirer/confirm": "^5.1.14", + "@inquirer/editor": "^4.2.15", + "@inquirer/expand": "^4.0.17", + "@inquirer/input": "^4.2.1", + "@inquirer/number": "^3.0.17", + "@inquirer/password": "^4.0.17", + "@inquirer/rawlist": "^4.1.5", + "@inquirer/search": "^3.1.0", + "@inquirer/select": "^4.3.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.9.tgz", + "integrity": "sha512-AWpxB7MuJrRiSfTKGJ7Y68imYt8P9N3Gaa7ySdkFj1iWjr6WfbGAhdZvw/UnhFXTHITJzxGUI9k8IX7akAEBCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.0.tgz", + "integrity": "sha512-a5SzB/qrXafDX1Z4AZW3CsVoiNxcIYCzYP7r9RzrfMpaLpB+yWi5U8BWagZyLmwR0pKbbL5umnGRd0RzGVI8bQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.0.tgz", + "integrity": "sha512-kaC3FHsJZvVyIjYBs5Ih8y8Bj4P/QItQWrZW22WJax7zTN+ZPXVGuOM55vzbdCP9zKUiBd9iEJVdesujfF+cAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.9.tgz", + "integrity": "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@nestjs-modules/mailer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@nestjs-modules/mailer/-/mailer-2.0.2.tgz", + "integrity": "sha512-+z4mADQasg0H1ZaGu4zZTuKv2pu+XdErqx99PLFPzCDNTN/q9U59WPgkxVaHnsvKHNopLj5Xap7G4ZpptduoYw==", + "license": "MIT", + "dependencies": { + "@css-inline/css-inline": "0.14.1", + "glob": "10.3.12" + }, + "optionalDependencies": { + "@types/ejs": "^3.1.5", + "@types/mjml": "^4.7.4", + "@types/pug": "^2.0.10", + "ejs": "^3.1.10", + "handlebars": "^4.7.8", + "liquidjs": "^10.11.1", + "mjml": "^4.15.3", + "preview-email": "^3.0.19", + "pug": "^3.0.2" + }, + "peerDependencies": { + "@nestjs/common": ">=7.0.9", + "@nestjs/core": ">=7.0.9", + "@types/ejs": ">=3.0.3", + "@types/mjml": ">=4.7.4", + "@types/pug": ">=2.0.6", + "ejs": ">=3.1.2", + "handlebars": ">=4.7.6", + "liquidjs": ">=10.8.2", + "mjml": ">=4.15.3", + "nodemailer": ">=6.4.6", + "preview-email": ">=3.0.19", + "pug": ">=3.0.1" + } + }, + "node_modules/@nestjs-modules/mailer/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@nestjs-modules/mailer/node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nestjs-modules/mailer/node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@nestjs-modules/mailer/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nestjs-modules/mailer/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@nestjs/cli": { + "version": "11.0.10", + "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.10.tgz", + "integrity": "sha512-4waDT0yGWANg0pKz4E47+nUrqIJv/UqrZ5wLPkCqc7oMGRMWKAaw1NDZ9rKsaqhqvxb2LfI5+uXOWr4yi94DOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.15", + "@angular-devkit/schematics": "19.2.15", + "@angular-devkit/schematics-cli": "19.2.15", + "@inquirer/prompts": "7.8.0", + "@nestjs/schematics": "^11.0.1", + "ansis": "4.1.0", + "chokidar": "4.0.3", + "cli-table3": "0.6.5", + "commander": "4.1.1", + "fork-ts-checker-webpack-plugin": "9.1.0", + "glob": "11.0.3", + "node-emoji": "1.11.0", + "ora": "5.4.1", + "tree-kill": "1.2.2", + "tsconfig-paths": "4.2.0", + "tsconfig-paths-webpack-plugin": "4.2.0", + "typescript": "5.8.3", + "webpack": "5.100.2", + "webpack-node-externals": "3.0.0" + }, + "bin": { + "nest": "bin/nest.js" + }, + "engines": { + "node": ">= 20.11" + }, + "peerDependencies": { + "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0", + "@swc/core": "^1.3.62" + }, + "peerDependenciesMeta": { + "@swc/cli": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/@nestjs/cli/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@nestjs/cli/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@nestjs/cli/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/@nestjs/cli/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@nestjs/cli/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@nestjs/cli/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nestjs/cli/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/@nestjs/cli/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/@nestjs/cli/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@nestjs/cli/node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@nestjs/cli/node_modules/webpack": { + "version": "5.100.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.100.2.tgz", + "integrity": "sha512-QaNKAvGCDRh3wW1dsDjeMdDXwZm2vqq3zn6Pvq4rHOEOGSaUMgOOjG2Y9ZbIGzpfkJk9ZYTHpDqgDfeBDcnLaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.2", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@nestjs/common": { + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.8.tgz", + "integrity": "sha512-bbsOqwld/GdBfiRNc4nnjyWWENDEicq4SH+R5AuYatvf++vf1x5JIsHB1i1KtfZMD3eRte0D4K9WXuAYil6XAg==", + "license": "MIT", + "peer": true, + "dependencies": { + "file-type": "21.0.0", + "iterare": "1.2.1", + "load-esm": "1.0.3", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "class-transformer": ">=0.4.1", + "class-validator": ">=0.13.2", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.2.tgz", + "integrity": "sha512-McMW6EXtpc8+CwTUwFdg6h7dYcBUpH5iUILCclAsa+MbCEvC9ZKu4dCHRlJqALuhjLw97pbQu62l4+wRwGeZqA==", + "license": "MIT", + "dependencies": { + "dotenv": "16.4.7", + "dotenv-expand": "12.0.1", + "lodash": "4.17.21" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/config/node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/@nestjs/core": { + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.8.tgz", + "integrity": "sha512-7riWfmTmMhCJHZ5ZiaG+crj4t85IPCq/wLRuOUSigBYyFT2JZj0lVHtAdf4Davp9ouNI8GINBDt9h9b5Gz9nTw==", + "hasInstallScript": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@nuxt/opencollective": "0.4.1", + "fast-safe-stringify": "2.1.1", + "iterare": "1.2.1", + "path-to-regexp": "8.3.0", + "tslib": "2.8.1", + "uid": "2.0.2" + }, + "engines": { + "node": ">= 20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/microservices": "^11.0.0", + "@nestjs/platform-express": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/microservices": { + "optional": true + }, + "@nestjs/platform-express": { + "optional": true + }, + "@nestjs/websockets": { + "optional": true + } + } + }, + "node_modules/@nestjs/jwt": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.1.tgz", + "integrity": "sha512-HXSsc7SAnCnjA98TsZqrE7trGtHDnYXWp4Ffy6LwSmck1QvbGYdMzBquXofX5l6tIRpeY4Qidl2Ti2CVG77Pdw==", + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "9.0.10", + "jsonwebtoken": "9.0.2" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0" + } + }, + "node_modules/@nestjs/mapped-types": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz", + "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "class-transformer": "^0.4.0 || ^0.5.0", + "class-validator": "^0.13.0 || ^0.14.0", + "reflect-metadata": "^0.1.12 || ^0.2.0" + }, + "peerDependenciesMeta": { + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + } + } + }, + "node_modules/@nestjs/passport": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-11.0.5.tgz", + "integrity": "sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "passport": "^0.5.0 || ^0.6.0 || ^0.7.0" + } + }, + "node_modules/@nestjs/platform-express": { + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.8.tgz", + "integrity": "sha512-rL6pZH9BW7BnL5X2eWbJMtt86uloAKjFgyY5+L2UkizgfEp7rgAs0+Z1z0BcW2Pgu5+q8O7RKPNyHJ/9ZNz/ZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "cors": "2.8.5", + "express": "5.1.0", + "multer": "2.0.2", + "path-to-regexp": "8.3.0", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0" + } + }, + "node_modules/@nestjs/platform-socket.io": { + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.8.tgz", + "integrity": "sha512-nMUvwcdztso8BjN9czRl4sm0Ewc5xrCcgLvy+QPt6VAnTdu06KcZqtA6Cl3MKxViSQsQ8NBN5foKvZehNt/tug==", + "license": "MIT", + "peer": true, + "dependencies": { + "socket.io": "4.8.1", + "tslib": "2.8.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nest" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/websockets": "^11.0.0", + "rxjs": "^7.1.0" + } + }, + "node_modules/@nestjs/schedule": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.1.0.tgz", + "integrity": "sha512-W25Ydc933Gzb1/oo7+bWzzDiOissE+h/dhIAPugA39b9MuIzBbLybuXpc1AjoQLczO3v0ldmxaffVl87W0uqoQ==", + "license": "MIT", + "dependencies": { + "cron": "4.3.5" + }, + "peerDependencies": { + "@nestjs/common": "^10.0.0 || ^11.0.0", + "@nestjs/core": "^10.0.0 || ^11.0.0" + } + }, + "node_modules/@nestjs/schematics": { + "version": "11.0.9", + "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz", + "integrity": "sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.17", + "@angular-devkit/schematics": "19.2.17", + "comment-json": "4.4.1", + "jsonc-parser": "3.3.1", + "pluralize": "8.0.0" + }, + "peerDependencies": { + "typescript": ">=4.8.2" + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.17.tgz", + "integrity": "sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.17.tgz", + "integrity": "sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.17", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@nestjs/schematics/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@nestjs/schematics/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nestjs/schematics/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@nestjs/websockets": { + "version": "11.1.8", + "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.8.tgz", + "integrity": "sha512-RXo2336p/vyAwJ0qPInglzNSQ//qz+JTLr2LE1vlbmN5WcyB7zV6+gY06YgNdsr3oy/cXRh7fnC3Ph/VAu1EVg==", + "license": "MIT", + "peer": true, + "dependencies": { + "iterare": "1.2.1", + "object-hash": "3.0.0", + "tslib": "2.8.1" + }, + "peerDependencies": { + "@nestjs/common": "^11.0.0", + "@nestjs/core": "^11.0.0", + "@nestjs/platform-socket.io": "^11.0.0", + "reflect-metadata": "^0.1.12 || ^0.2.0", + "rxjs": "^7.1.0" + }, + "peerDependenciesMeta": { + "@nestjs/platform-socket.io": { + "optional": true + } + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nuxt/opencollective": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", + "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + }, + "bin": { + "opencollective": "bin/opencollective.js" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0", + "npm": ">=5.10.0" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "license": "MIT", + "optional": true + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@prisma/client": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.0.tgz", + "integrity": "sha512-QXFT+N/bva/QI2qoXmjBzL7D6aliPffIwP+81AdTGq0FXDoLxLkWivGMawG8iM5B9BKfxLIXxfWWAF6wbuJU6g==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.0.tgz", + "integrity": "sha512-zwCayme+NzI/WfrvFEtkFhhOaZb/hI+X8TTjzjJ252VbPxAl2hWHK5NMczmnG9sXck2lsXrxIZuK524E25UNmg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.0.tgz", + "integrity": "sha512-8hAdGG7JmxrzFcTzXZajlQCidX0XNkMJkpqtfbLV54wC6LSSX6Vni25W/G+nAANwLnZ2TmwkfIuWetA7jJxJFA==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.0.tgz", + "integrity": "sha512-pMRJ+1S6NVdXoB8QJAPIGpKZevFjxhKt0paCkRDTZiczKb7F4yTgRP8M4JdVkpQwmaD4EoJf6qA+p61godDokw==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.0", + "@prisma/engines-version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", + "@prisma/fetch-engine": "6.19.0", + "@prisma/get-platform": "6.19.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773.tgz", + "integrity": "sha512-gV7uOBQfAFlWDvPJdQxMT1aSRur3a0EkU/6cfbAC5isV67tKDWUrPauyaHNpB+wN1ebM4A9jn/f4gH+3iHSYSQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.0.tgz", + "integrity": "sha512-OOx2Lda0DGrZ1rodADT06ZGqHzr7HY7LNMaFE2Vp8dp146uJld58sRuasdX0OiwpHgl8SqDTUKHNUyzEq7pDdQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.0", + "@prisma/engines-version": "6.19.0-26.2ba551f319ab1df4bc874a89965d8b3641056773", + "@prisma/get-platform": "6.19.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.0.tgz", + "integrity": "sha512-ym85WDO2yDhC3fIXHWYpG3kVMBA49cL1XD2GCsCF8xbwoy2OkDQY44gEbAt2X46IQ4Apq9H6g0Ex1iFfPqEkHA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.0" + } + }, + "node_modules/@selderee/plugin-htmlparser2": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", + "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "domhandler": "^5.0.3", + "selderee": "^0.11.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.5.tgz", + "integrity": "sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.3.tgz", + "integrity": "sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-config-provider": "^4.2.0", + "@smithy/util-endpoints": "^3.2.5", + "@smithy/util-middleware": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "3.18.7", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.18.7.tgz", + "integrity": "sha512-axG9MvKhMWOhFbvf5y2DuyTxQueO0dkedY9QC3mAfndLosRI/9LJv8WaL0mw7ubNhsO4IuXX9/9dYGPFvHrqlw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-serde": "^4.2.6", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-body-length-browser": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-stream": "^4.5.6", + "@smithy/util-utf8": "^4.2.0", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.5.tgz", + "integrity": "sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.6.tgz", + "integrity": "sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/hash-node": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.5.tgz", + "integrity": "sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.5.tgz", + "integrity": "sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", + "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.5.tgz", + "integrity": "sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.14.tgz", + "integrity": "sha512-v0q4uTKgBM8dsqGjqsabZQyH85nFaTnFcgpWU1uydKFsdyyMzfvOkNum9G7VK+dOP01vUnoZxIeRiJ6uD0kjIg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.18.7", + "@smithy/middleware-serde": "^4.2.6", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "@smithy/url-parser": "^4.2.5", + "@smithy/util-middleware": "^4.2.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "4.4.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.14.tgz", + "integrity": "sha512-Z2DG8Ej7FyWG1UA+7HceINtSLzswUgs2np3sZX0YBBxCt+CXG4QUxv88ZDS3+2/1ldW7LqtSY1UO/6VQ1pND8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/service-error-classification": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-retry": "^4.2.5", + "@smithy/uuid": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.6.tgz", + "integrity": "sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.5.tgz", + "integrity": "sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.5.tgz", + "integrity": "sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.5", + "@smithy/shared-ini-file-loader": "^4.4.0", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.5.tgz", + "integrity": "sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/abort-controller": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/querystring-builder": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.5.tgz", + "integrity": "sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.5.tgz", + "integrity": "sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.5.tgz", + "integrity": "sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "@smithy/util-uri-escape": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.5.tgz", + "integrity": "sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.5.tgz", + "integrity": "sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.0.tgz", + "integrity": "sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.5.tgz", + "integrity": "sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-middleware": "^4.2.5", + "@smithy/util-uri-escape": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "4.9.10", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.10.tgz", + "integrity": "sha512-Jaoz4Jw1QYHc1EFww/E6gVtNjhoDU+gwRKqXP6C3LKYqqH2UQhP8tMP3+t/ePrhaze7fhLE8vS2q6vVxBANFTQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^3.18.7", + "@smithy/middleware-endpoint": "^4.3.14", + "@smithy/middleware-stack": "^4.2.5", + "@smithy/protocol-http": "^5.3.5", + "@smithy/types": "^4.9.0", + "@smithy/util-stream": "^4.5.6", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.9.0.tgz", + "integrity": "sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.5.tgz", + "integrity": "sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-base64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", + "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", + "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", + "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", + "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", + "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "4.3.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.13.tgz", + "integrity": "sha512-hlVLdAGrVfyNei+pKIgqDTxfu/ZI2NSyqj4IDxKd5bIsIqwR/dSlkxlPaYxFiIaDVrBy0he8orsFy+Cz119XvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "4.2.16", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.16.tgz", + "integrity": "sha512-F1t22IUiJLHrxW9W1CQ6B9PN+skZ9cqSuzB18Eh06HrJPbjsyZ7ZHecAKw80DQtyGTRcVfeukKaCRYebFwclbg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/config-resolver": "^4.4.3", + "@smithy/credential-provider-imds": "^4.2.5", + "@smithy/node-config-provider": "^4.3.5", + "@smithy/property-provider": "^4.2.5", + "@smithy/smithy-client": "^4.9.10", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.5.tgz", + "integrity": "sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^4.3.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", + "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.5.tgz", + "integrity": "sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.5.tgz", + "integrity": "sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^4.2.5", + "@smithy/types": "^4.9.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.6.tgz", + "integrity": "sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/fetch-http-handler": "^5.3.6", + "@smithy/node-http-handler": "^4.4.5", + "@smithy/types": "^4.9.0", + "@smithy/util-base64": "^4.3.0", + "@smithy/util-buffer-from": "^4.2.0", + "@smithy/util-hex-encoding": "^4.2.0", + "@smithy/util-utf8": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", + "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", + "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^4.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@smithy/uuid": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", + "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookie-parser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.10.tgz", + "integrity": "sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cron": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/cron/-/cron-2.0.1.tgz", + "integrity": "sha512-WHa/1rtNtD2Q/H0+YTTZoty+/5rcE66iAFX2IY+JuUoOACsevYyFkSYu/2vdw+G5LrmO7Lxowrqm0av4k3qWNQ==", + "license": "MIT", + "dependencies": { + "@types/luxon": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ejs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", + "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "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/@types/express": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.5.tgz", + "integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^1" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz", + "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/luxon": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==", + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "license": "MIT" + }, + "node_modules/@types/mjml": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/@types/mjml/-/mjml-4.7.4.tgz", + "integrity": "sha512-vyi1vzWgMzFMwZY7GSZYX0GU0dmtC8vLHwpgk+NWmwbwRSrlieVyJ9sn5elodwUfklJM7yGl0zQeet1brKTWaQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/mjml-core": "*" + } + }, + "node_modules/@types/mjml-core": { + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/@types/mjml-core/-/mjml-core-4.15.2.tgz", + "integrity": "sha512-Q7SxFXgoX979HP57DEVsRI50TV8x1V4lfCA4Up9AvfINDM5oD/X9ARgfoyX1qS987JCnDLv85JjkqAjt3hZSiQ==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/multer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz", + "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==", + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/node": { + "version": "22.19.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.0.tgz", + "integrity": "sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/nodemailer": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.4.tgz", + "integrity": "sha512-ee8fxWqOchH+Hv6MDDNNy028kwvVnLplrStm4Zf/3uHWw5zzo8FoYYeffpJtGs2wWysEumMH0ZIdMGMY1eMAow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@aws-sdk/client-sesv2": "^3.839.0", + "@types/node": "*" + } + }, + "node_modules/@types/passport": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", + "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "*", + "@types/passport-strategy": "*" + } + }, + "node_modules/@types/passport-strategy": { + "version": "0.2.38", + "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", + "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, + "node_modules/@types/pug": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", + "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/validator": { + "version": "13.15.4", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.4.tgz", + "integrity": "sha512-LSFfpSnJJY9wbC0LQxgvfb+ynbHftFo0tMsFOl/J4wexLnYMmDSPaj2ZyDv3TkfL1UePxPrxOWJfbiRS8mQv7A==", + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.3.tgz", + "integrity": "sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/type-utils": "8.46.3", + "@typescript-eslint/utils": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.46.3", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.3.tgz", + "integrity": "sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.3.tgz", + "integrity": "sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.46.3", + "@typescript-eslint/types": "^8.46.3", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.3.tgz", + "integrity": "sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.3.tgz", + "integrity": "sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.3.tgz", + "integrity": "sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/utils": "8.46.3", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.3.tgz", + "integrity": "sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.3.tgz", + "integrity": "sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.46.3", + "@typescript-eslint/tsconfig-utils": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/visitor-keys": "8.46.3", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.3.tgz", + "integrity": "sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.46.3", + "@typescript-eslint/types": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.3.tgz", + "integrity": "sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.46.3", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@zone-eu/mailsplit": { + "version": "5.4.7", + "resolved": "https://registry.npmjs.org/@zone-eu/mailsplit/-/mailsplit-5.4.7.tgz", + "integrity": "sha512-jApX86aDgolMz08pP20/J2zcns02NSK3zSiYouf01QQg4250L+GUAWSWicmS7eRvs+Z7wP7QfXrnkaTBGrIpwQ==", + "license": "(MIT OR EUPL-1.1+)", + "optional": true, + "dependencies": { + "libbase64": "1.3.0", + "libmime": "5.3.7", + "libqp": "2.1.1" + } + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "license": "ISC", + "optional": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "peer": true, + "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/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/alce": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/alce/-/alce-1.2.0.tgz", + "integrity": "sha512-XppPf2S42nO2WhvKzlwzlfcApcXHzjlod30pKmcWjRgLOtqoe5DMuqdiYoM6AgyXksc6A6pV4v1L/WW217e57w==", + "license": "MIT", + "optional": true, + "dependencies": { + "esprima": "^1.2.0", + "estraverse": "^1.5.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/alce/node_modules/esprima": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz", + "integrity": "sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==", + "optional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/alce/node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.1.0.tgz", + "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "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==", + "license": "ISC", + "optional": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT", + "optional": true + }, + "node_modules/assert-never": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz", + "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==", + "license": "MIT", + "optional": true + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT", + "optional": true + }, + "node_modules/babel-walk": { + "version": "3.0.0-canary-5", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/types": "^7.9.6" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.25", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz", + "integrity": "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC", + "optional": true + }, + "node_modules/bowser": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", + "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "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/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "license": "MIT", + "optional": true, + "dependencies": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001754", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", + "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/character-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-regex": "^1.0.3" + } + }, + "node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT", + "peer": true + }, + "node_modules/class-validator": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.2.tgz", + "integrity": "sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.11.1", + "validator": "^13.9.0" + } + }, + "node_modules/clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "license": "MIT", + "optional": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/comment-json": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", + "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/constantinople": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.1" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "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-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cron": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/cron/-/cron-4.3.5.tgz", + "integrity": "sha512-hKPP7fq1+OfyCqoePkKfVq7tNAdFwiQORr4lZUHwrf0tebC65fYEeWgOrXOL6prn1/fegGOdTfrM6e34PJfksg==", + "license": "MIT", + "dependencies": { + "@types/luxon": "~3.7.0", + "luxon": "~3.7.0" + }, + "engines": { + "node": ">=18.x" + } + }, + "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/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "devOptional": true, + "license": "MIT" + }, + "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/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "license": "MIT", + "optional": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/display-notification": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/display-notification/-/display-notification-2.0.0.tgz", + "integrity": "sha512-TdmtlAcdqy1NU+j7zlkDdMnCL878zriLaBmoD9quOoq1ySSSGv03l0hXK5CvIFZlIfFI/hizqdQuW+Num7xuhw==", + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-applescript": "^1.0.0", + "run-applescript": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/doctypes": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==", + "license": "MIT", + "optional": true + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "optional": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.1.tgz", + "integrity": "sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==", + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "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/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "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/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.245", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.245.tgz", + "integrity": "sha512-rdmGfW47ZhL/oWEJAY4qxRtdly2B98ooTJ0pdEI4jhVLZ6tNf8fPtov2wS1IRKwFJT92le3x4Knxiwzl7cPPpQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "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/encoding-japanese": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz", + "integrity": "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/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/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/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/engine.io/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/engine.io/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/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "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-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/escape-string-applescript": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/escape-string-applescript/-/escape-string-applescript-1.0.0.tgz", + "integrity": "sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "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/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "license": "MIT", + "optional": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "license": "MIT", + "optional": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/execa/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "license": "MIT", + "optional": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "optional": true + }, + "node_modules/execa/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/extend-object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extend-object/-/extend-object-1.0.0.tgz", + "integrity": "sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw==", + "license": "MIT", + "optional": true + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fast-check/node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "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-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/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/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/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/file-type": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.0.0.tgz", + "integrity": "sha512-ek5xNX2YBYlXhiUXui3D/BXa3LdqPmoLJ7rqEx2bKJ7EAUEfmXgW0Das7Dc6Nr9MvqaOnIqiPV0mZk/r/UpNAg==", + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.2.7", + "strtok3": "^10.2.2", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fixpack": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fixpack/-/fixpack-4.0.0.tgz", + "integrity": "sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "alce": "1.2.0", + "chalk": "^3.0.0", + "detect-indent": "^6.0.0", + "detect-newline": "^3.1.0", + "extend-object": "^1.0.0", + "rc": "^1.2.8" + }, + "bin": { + "fixpack": "bin/fixpack" + } + }, + "node_modules/fixpack/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fork-ts-checker-webpack-plugin": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", + "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.16.7", + "chalk": "^4.1.2", + "chokidar": "^4.0.1", + "cosmiconfig": "^8.2.0", + "deepmerge": "^4.2.2", + "fs-extra": "^10.0.0", + "memfs": "^3.4.1", + "minimatch": "^3.0.4", + "node-abort-controller": "^3.0.1", + "schema-utils": "^3.1.1", + "semver": "^7.3.5", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "typescript": ">3.6.0", + "webpack": "^5.11.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-monkey": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", + "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "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-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "optional": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "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-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "optional": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "optional": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-minifier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz", + "integrity": "sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==", + "license": "MIT", + "optional": true, + "dependencies": { + "camel-case": "^3.0.0", + "clean-css": "^4.2.1", + "commander": "^2.19.0", + "he": "^1.2.0", + "param-case": "^2.1.1", + "relateurl": "^0.2.7", + "uglify-js": "^3.5.1" + }, + "bin": { + "html-minifier": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/html-minifier/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT", + "optional": true + }, + "node_modules/html-to-text": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", + "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@selderee/plugin-htmlparser2": "^0.11.0", + "deepmerge": "^4.3.1", + "dom-serializer": "^2.0.0", + "htmlparser2": "^8.0.2", + "selderee": "^0.11.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC", + "optional": true + }, + "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-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "optional": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "optional": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-expression": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", + "license": "MIT", + "optional": true, + "dependencies": { + "acorn": "^7.1.1", + "object-assign": "^4.1.1" + } + }, + "node_modules/is-expression/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "license": "MIT", + "optional": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "optional": true, + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/iterare": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", + "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", + "license": "ISC", + "engines": { + "node": ">=6" + } + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "license": "MIT", + "optional": true, + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "optional": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "optional": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "optional": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz", + "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==", + "license": "MIT", + "optional": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "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-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "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/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } + }, + "node_modules/jstransformer/node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT", + "optional": true + }, + "node_modules/juice": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/juice/-/juice-10.0.1.tgz", + "integrity": "sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA==", + "license": "MIT", + "optional": true, + "dependencies": { + "cheerio": "1.0.0-rc.12", + "commander": "^6.1.0", + "mensch": "^0.3.4", + "slick": "^1.12.2", + "web-resource-inliner": "^6.0.1" + }, + "bin": { + "juice": "bin/juice" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/juice/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "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": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leac": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", + "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", + "license": "MIT", + "optional": true, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/libbase64": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", + "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==", + "license": "MIT", + "optional": true + }, + "node_modules/libmime": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.7.tgz", + "integrity": "sha512-FlDb3Wtha8P01kTL3P9M+ZDNDWPKPmKHWaU/cG/lg5pfuAwdflVpZE+wm9m7pKmC5ww6s+zTxBKS1p6yl3KpSw==", + "license": "MIT", + "optional": true, + "dependencies": { + "encoding-japanese": "2.2.0", + "iconv-lite": "0.6.3", + "libbase64": "1.3.0", + "libqp": "2.1.1" + } + }, + "node_modules/libmime/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/libphonenumber-js": { + "version": "1.12.25", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.25.tgz", + "integrity": "sha512-u90tUu/SEF8b+RaDKCoW7ZNFDakyBtFlX1ex3J+VH+ElWes/UaitJLt/w4jGu8uAE41lltV/s+kMVtywcMEg7g==", + "license": "MIT" + }, + "node_modules/libqp": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz", + "integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==", + "license": "MIT", + "optional": true + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/liquidjs": { + "version": "10.24.0", + "resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.24.0.tgz", + "integrity": "sha512-TAUNAdgwaAXjjcUFuYVJm9kOVH7zc0mTKxsG9t9Lu4qdWjB2BEblyVIYpjWcmJLMGgiYqnGNJjpNMHx0gp/46A==", + "license": "MIT", + "optional": true, + "dependencies": { + "commander": "^10.0.0" + }, + "bin": { + "liquid": "bin/liquid.js", + "liquidjs": "bin/liquid.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/liquidjs" + } + }, + "node_modules/liquidjs/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/load-esm": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", + "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + }, + { + "type": "buymeacoffee", + "url": "https://buymeacoffee.com/borewit" + } + ], + "license": "MIT", + "engines": { + "node": ">=13.2.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "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/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", + "license": "MIT", + "optional": true + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/mailparser": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.9.0.tgz", + "integrity": "sha512-jpaNLhDjwy0w2f8sySOSRiWREjPqssSc0C2czV98btCXCRX3EyNloQ2IWirmMDj1Ies8Fkm0l96bZBZpDG7qkg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@zone-eu/mailsplit": "5.4.7", + "encoding-japanese": "2.2.0", + "he": "1.2.0", + "html-to-text": "9.0.5", + "iconv-lite": "0.7.0", + "libmime": "5.3.7", + "linkify-it": "5.0.0", + "nodemailer": "7.0.10", + "punycode.js": "2.3.1", + "tlds": "1.261.0" + } + }, + "node_modules/mailparser/node_modules/nodemailer": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.10.tgz", + "integrity": "sha512-Us/Se1WtT0ylXgNFfyFSx4LElllVLJXQjWi2Xz17xWw7amDKO2MLtFnVp1WACy7GkVGs+oBlRopVNUzlrGSw1w==", + "license": "MIT-0", + "optional": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/mensch": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz", + "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==", + "license": "MIT", + "optional": true + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "license": "MIT", + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mjml": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml/-/mjml-4.17.2.tgz", + "integrity": "sha512-hAx9azG9lk/fQg6HuvMlc7IcgYLkXqT730psppHf8vGZ4L7j0Wr5NRq0B9Dm+aon6b+1WZrelPO9mIxnjgYiKA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "mjml-cli": "4.17.2", + "mjml-core": "4.17.2", + "mjml-migrate": "4.17.2", + "mjml-preset-core": "4.17.2", + "mjml-validator": "4.17.2" + }, + "bin": { + "mjml": "bin/mjml" + } + }, + "node_modules/mjml-accordion": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-accordion/-/mjml-accordion-4.17.2.tgz", + "integrity": "sha512-faepE2yYKrpJxSHluQZV13D8ibqEar5SsPmT//MTnE0s0kW1KvEOaY+oXdgSGBbWWnvdvNQm4APxRz5yDCueUQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-body": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-body/-/mjml-body-4.17.2.tgz", + "integrity": "sha512-Tn6jLBHYY84JnM5iK/M6pIhaDxxQ8OP6DqBp89EJeTkmad1AgJsrF81kqeyK4r4FQb/Iz6ShitpX/ICfKSJjTA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-button": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-button/-/mjml-button-4.17.2.tgz", + "integrity": "sha512-BMgHaOl4X93QAu0zEezycLIK/YbTrB1wZYGqjZeW8OxCOcC6Pqq1R3JiHofz0mmzJHohu9SC5ZFKJwyqN16Obg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-carousel": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-carousel/-/mjml-carousel-4.17.2.tgz", + "integrity": "sha512-Jj2/ZqlH+pa5hAT8FrVPTzb+reY0dgo4ceYBeTDc4zzoVSO5CK/83/Sb/WQlYhiJHTbaV1pTX8l99T8/uXoULw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-cli": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-cli/-/mjml-cli-4.17.2.tgz", + "integrity": "sha512-v5/7aTbcB0em2iyZS6+ikulUQOu1nkHhMELIjN0sYfw+7y2kcM/2WKqE8mVv97Tf4qQcIYsX4Oh6rBvZ2HOfXg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "chokidar": "^3.0.0", + "glob": "^10.3.10", + "html-minifier": "^4.0.0", + "js-beautify": "^1.6.14", + "lodash": "^4.17.21", + "minimatch": "^9.0.3", + "mjml-core": "4.17.2", + "mjml-migrate": "4.17.2", + "mjml-parser-xml": "4.17.2", + "mjml-validator": "4.17.2", + "yargs": "^17.7.2" + }, + "bin": { + "mjml-cli": "bin/mjml" + } + }, + "node_modules/mjml-cli/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mjml-cli/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "optional": true, + "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/mjml-cli/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "license": "ISC", + "optional": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mjml-cli/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==", + "license": "ISC", + "optional": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mjml-cli/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "optional": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/mjml-cli/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mjml-cli/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "optional": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mjml-cli/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mjml-cli/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "optional": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/mjml-column": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-column/-/mjml-column-4.17.2.tgz", + "integrity": "sha512-JYOiSwiP+/XPX/sPMj2l10X6fCBqL0HFardJ2XsgYrgYApgo+lQvuK4xHI7vEm5t3Fhu76ZPdrEOsqPQ2+liGg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-core": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-core/-/mjml-core-4.17.2.tgz", + "integrity": "sha512-4gpL4bMZ7XsNGUA5KTWtpZ7RQMeFRHTdmsfwnwzGCZ4lWnvXoK54vAWeYajN1ohxdwIImcDmUfsC935Pi3FqNg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "cheerio": "1.0.0-rc.12", + "detect-node": "^2.0.4", + "html-minifier": "^4.0.0", + "js-beautify": "^1.6.14", + "juice": "^10.0.0", + "lodash": "^4.17.21", + "mjml-migrate": "4.17.2", + "mjml-parser-xml": "4.17.2", + "mjml-validator": "4.17.2" + } + }, + "node_modules/mjml-divider": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-divider/-/mjml-divider-4.17.2.tgz", + "integrity": "sha512-U88vPMN9nWO6YEz2OrNiKUchoViLxauNP77Z8eZPrhTEHfAWHWi2KBUk7or77dC4/b/laQs5/12dzKGUFoOY/A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-group": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-group/-/mjml-group-4.17.2.tgz", + "integrity": "sha512-DZbeToAsynCjF5iD8Uzcuep/M3+WMAjvNhIdODrGjC6KGDeDc1fRoHBWV509MJxmMNH8S3D/theNJjiS6U4BLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-head": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-head/-/mjml-head-4.17.2.tgz", + "integrity": "sha512-VQb0G59frvSKoQYJJNDotCYcYL3RwMwHtdu+FhkK6EQ5D0JnapLLCYRD0iEuRciZR4ac+2ghg6jYcbJiAntwLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-head-attributes": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-head-attributes/-/mjml-head-attributes-4.17.2.tgz", + "integrity": "sha512-HcKdm+5DTc84kbkx9Z3w9wTLlJwyAlTEt9HHk4LA334Xve4/psd47mLnshYasLeeYj/nPV/Qx83uGxzyIOW8mA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-head-breakpoint": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-head-breakpoint/-/mjml-head-breakpoint-4.17.2.tgz", + "integrity": "sha512-6cqz2b9xUT4XZyl/ykmnFuwjupVn7Zlopz4clrapiPMLOvb8w2aTBK+Al+ttutHSjJde5lgpkzcs9V2QlnBmgg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-head-font": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-head-font/-/mjml-head-font-4.17.2.tgz", + "integrity": "sha512-Jm8B2yiOMWB7u1mp3CMVvlFhYdnNijk6M/UTuMzXikZ4Uwx46upbvafZf0Ki2nDYRRCqke3lZxvuG1+9W4xDnw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-head-html-attributes": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-head-html-attributes/-/mjml-head-html-attributes-4.17.2.tgz", + "integrity": "sha512-7zXxCDJJXQprP691yEK8SpoF1gHOeNMFnr/iJb3d8v7Aw9Rzt/7rAjtVd9LCeYvzaeCSodAbz/pAc8yg4VDbrg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-head-preview": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-head-preview/-/mjml-head-preview-4.17.2.tgz", + "integrity": "sha512-ihpVcwJnUcnH5f9znzNY0Mw0izQ29WA3gJIVRww5lGj25IC+RaElwzvUBsuxrx1WBhcIAjbaL4Em+5aNSIhP8Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-head-style": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-head-style/-/mjml-head-style-4.17.2.tgz", + "integrity": "sha512-iSrbIUml3AdqWy/cMA9xDZj37Mu3H8XKO6S/kdaTr/BlprnLXoGz3BTHH3lJu7e6tXrhqe2vT+4Gjngh/wfW9A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-head-title": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-head-title/-/mjml-head-title-4.17.2.tgz", + "integrity": "sha512-9NuyGzfkVHE8T81frxyYEPcjVoedQKPZ/W+hBMeBAV1KtlMT+/fAXlksrHq0rIiWXrewVv4Vl9iIOOAcG4ABYw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-hero": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-hero/-/mjml-hero-4.17.2.tgz", + "integrity": "sha512-7r8uqAmqBpqBsLRgC2Am+iUFlBg2AMB3aYzhajJ4pqgaqotPUuumdYNM3j9i8DfBFhTRGeFLjQXrUJuzBmmBOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-image": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-image/-/mjml-image-4.17.2.tgz", + "integrity": "sha512-hB4viCuqQ9h69VFksOgIekf2KgurHIOmIR9NJVrjTErhCFZZ1HJXOUgPzNhyDzPWZ69/7RyT1icIXC2v6g5U3Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-migrate": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-migrate/-/mjml-migrate-4.17.2.tgz", + "integrity": "sha512-a8klJLvelnNK8JErpgcvgXFaNUpfJe8zjnuQD6Z93edAKYGArZ/DFlUk4XJAUY6mXqr7Yd45Ch3XJeyI9Crheg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "js-beautify": "^1.6.14", + "lodash": "^4.17.21", + "mjml-core": "4.17.2", + "mjml-parser-xml": "4.17.2", + "yargs": "^17.7.2" + }, + "bin": { + "migrate": "lib/cli.js" + } + }, + "node_modules/mjml-navbar": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-navbar/-/mjml-navbar-4.17.2.tgz", + "integrity": "sha512-FiUZmpdjni6mRNkuFoVNFesTvHp7iAt7EBmfV+N3mAEkvqObV/A5bZYs1mmWPGoISE/FDVQ8O9wuPE95KWA+VA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-parser-xml": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-parser-xml/-/mjml-parser-xml-4.17.2.tgz", + "integrity": "sha512-/9pKKFeAbjwpsXLSGSxQv8/ZpblUbURc2Me5/REnIyGpwvccYYoCyTt5smVaewaWfawzNPYPlNKbqt9Sk00HLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "detect-node": "2.1.0", + "htmlparser2": "^9.1.0", + "lodash": "^4.17.21" + } + }, + "node_modules/mjml-parser-xml/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/mjml-preset-core": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-preset-core/-/mjml-preset-core-4.17.2.tgz", + "integrity": "sha512-5fAUKkxbDHNvarEdCCWEsWnyBJE9th6UP0tDKQKx0Lhk+yc/rDDbUTDLxZMPgYUHFihvkCeNOjuETv/NWuP3Qg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "mjml-accordion": "4.17.2", + "mjml-body": "4.17.2", + "mjml-button": "4.17.2", + "mjml-carousel": "4.17.2", + "mjml-column": "4.17.2", + "mjml-divider": "4.17.2", + "mjml-group": "4.17.2", + "mjml-head": "4.17.2", + "mjml-head-attributes": "4.17.2", + "mjml-head-breakpoint": "4.17.2", + "mjml-head-font": "4.17.2", + "mjml-head-html-attributes": "4.17.2", + "mjml-head-preview": "4.17.2", + "mjml-head-style": "4.17.2", + "mjml-head-title": "4.17.2", + "mjml-hero": "4.17.2", + "mjml-image": "4.17.2", + "mjml-navbar": "4.17.2", + "mjml-raw": "4.17.2", + "mjml-section": "4.17.2", + "mjml-social": "4.17.2", + "mjml-spacer": "4.17.2", + "mjml-table": "4.17.2", + "mjml-text": "4.17.2", + "mjml-wrapper": "4.17.2" + } + }, + "node_modules/mjml-raw": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-raw/-/mjml-raw-4.17.2.tgz", + "integrity": "sha512-S7G7Ut2U6xeq3rD48FY8AZoZEz9K5HVszipos0T6JYd0VxrjTKUSD5IvFX3nT+1QcG2ZJMSuO2gZm6SztPZdUg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-section": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-section/-/mjml-section-4.17.2.tgz", + "integrity": "sha512-PGR55DKVKDUioqCXZruol/TYT1j/zT0oLkwlQ6E2RBxErFJ33DJiNUNmAXXJ9N8gOh8iiuZJ3xwSMXZgJgJSbw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-social": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-social/-/mjml-social-4.17.2.tgz", + "integrity": "sha512-4/XlvSQDH0S9AWW80HEa08HFldAFAdNjYTV1TU5UuXbhFZrSIKx1i86Yh6Zb23fP0pbeb3yMlY5R8nrONYKRDQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-spacer": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-spacer/-/mjml-spacer-4.17.2.tgz", + "integrity": "sha512-FU5iYToqu8enC09X/mvQUPnHVCrmpSOvUr36qcuaduX+KbBgui8mACDJIvoDbiS4ikt7VOkxKy/C4GjzocKvKg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-table": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-table/-/mjml-table-4.17.2.tgz", + "integrity": "sha512-KyoCuskFRE0lk49RIoJ1zq2FOn7xG1ajJ1OQGoiMT4YZUq+w6aDHQs5OvUg5ZxdynBOHHEFhv4zsIE70s68JWg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-text": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-text/-/mjml-text-4.17.2.tgz", + "integrity": "sha512-K01V0iag2vV+3HOid1iCqmhKaBsMNJMB8jlZmI/3CXG6ckgglEvYjF1d1giiX2K5A4Y1ZmKwYLvsuhLXote5MA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2" + } + }, + "node_modules/mjml-validator": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-validator/-/mjml-validator-4.17.2.tgz", + "integrity": "sha512-XItJM6DlXPOHuvzSZhCXt5zxX6it8uXdnJQc1YY20hte6vfFDuKXe8tTRj66kQOZKhouhxm7EFvFRFmVd1vZtw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4" + } + }, + "node_modules/mjml-wrapper": { + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/mjml-wrapper/-/mjml-wrapper-4.17.2.tgz", + "integrity": "sha512-b292fkFyQC+8nQHzLhM3VzSfoE/CXCjeVMfFj/wjsg0gs7qxzRnKhIDZexr7nkgOAipt2B0vNh7bZisntY34Hw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.28.4", + "lodash": "^4.17.21", + "mjml-core": "4.17.2", + "mjml-section": "4.17.2" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", + "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/multer/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/multer/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "license": "MIT", + "optional": true + }, + "node_modules/no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "devOptional": true, + "license": "MIT" + }, + "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/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemailer": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.11.tgz", + "integrity": "sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw==", + "license": "MIT-0", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "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==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "license": "MIT", + "optional": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nypm": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", + "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.2", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "tinyexec": "^1.0.1" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.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-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "p-timeout": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "license": "MIT", + "optional": true, + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-wait-for": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.2.0.tgz", + "integrity": "sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "p-timeout": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "devOptional": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "license": "MIT", + "optional": true, + "dependencies": { + "no-case": "^2.2.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "optional": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "optional": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseley": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", + "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", + "license": "MIT", + "optional": true, + "dependencies": { + "leac": "^0.6.0", + "peberminta": "^0.9.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "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/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "license": "MIT", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=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-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT", + "optional": true + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, + "node_modules/peberminta": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", + "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", + "license": "MIT", + "optional": true, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/preview-email": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/preview-email/-/preview-email-3.1.0.tgz", + "integrity": "sha512-ZtV1YrwscEjlrUzYrTSs6Nwo49JM3pXLM4fFOBSC3wSni+bxaWlw9/Qgk75PZO8M7cX2EybmL2iwvaV3vkAttw==", + "license": "MIT", + "optional": true, + "dependencies": { + "ci-info": "^3.8.0", + "display-notification": "2.0.0", + "fixpack": "^4.0.0", + "get-port": "5.1.1", + "mailparser": "^3.7.1", + "nodemailer": "^6.9.13", + "open": "7", + "p-event": "4.2.0", + "p-wait-for": "3.2.0", + "pug": "^3.0.3", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/preview-email/node_modules/nodemailer": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.1.tgz", + "integrity": "sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==", + "license": "MIT-0", + "optional": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/prisma": { + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.0.tgz", + "integrity": "sha512-F3eX7K+tWpkbhl3l4+VkFtrwJlLXbAM+f9jolgoUZbFcm1DgHZ4cq9AgVEgUym2au5Ad/TDLN8lg83D+M10ycw==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@prisma/config": "6.19.0", + "@prisma/engines": "6.19.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "license": "MIT", + "optional": true, + "dependencies": { + "asap": "~2.0.3" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "license": "ISC", + "optional": true + }, + "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/pug": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.3.tgz", + "integrity": "sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==", + "license": "MIT", + "optional": true, + "dependencies": { + "pug-code-gen": "^3.0.3", + "pug-filters": "^4.0.0", + "pug-lexer": "^5.0.1", + "pug-linker": "^4.0.0", + "pug-load": "^3.0.0", + "pug-parser": "^6.0.0", + "pug-runtime": "^3.0.1", + "pug-strip-comments": "^2.0.0" + } + }, + "node_modules/pug-attrs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "license": "MIT", + "optional": true, + "dependencies": { + "constantinople": "^4.0.1", + "js-stringify": "^1.0.2", + "pug-runtime": "^3.0.0" + } + }, + "node_modules/pug-code-gen": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.3.tgz", + "integrity": "sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw==", + "license": "MIT", + "optional": true, + "dependencies": { + "constantinople": "^4.0.1", + "doctypes": "^1.1.0", + "js-stringify": "^1.0.2", + "pug-attrs": "^3.0.0", + "pug-error": "^2.1.0", + "pug-runtime": "^3.0.1", + "void-elements": "^3.1.0", + "with": "^7.0.0" + } + }, + "node_modules/pug-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz", + "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==", + "license": "MIT", + "optional": true + }, + "node_modules/pug-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "license": "MIT", + "optional": true, + "dependencies": { + "constantinople": "^4.0.1", + "jstransformer": "1.0.0", + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0", + "resolve": "^1.15.1" + } + }, + "node_modules/pug-lexer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "license": "MIT", + "optional": true, + "dependencies": { + "character-parser": "^2.2.0", + "is-expression": "^4.0.0", + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-linker": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "license": "MIT", + "optional": true, + "dependencies": { + "pug-error": "^2.0.0", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-load": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "object-assign": "^4.1.1", + "pug-walk": "^2.0.0" + } + }, + "node_modules/pug-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "license": "MIT", + "optional": true, + "dependencies": { + "pug-error": "^2.0.0", + "token-stream": "1.0.0" + } + }, + "node_modules/pug-runtime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz", + "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==", + "license": "MIT", + "optional": true + }, + "node_modules/pug-strip-comments": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "pug-error": "^2.0.0" + } + }, + "node_modules/pug-walk": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", + "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==", + "license": "MIT", + "optional": true + }, + "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/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-applescript": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-3.2.0.tgz", + "integrity": "sha512-Ep0RsvAjnRcBX1p5vogbaBdAGu/8j/ewpvGqnQYunnLd9SM0vWcPJewPKNnWFggf0hF0pwIgwV5XK7qQ7UZ8Qg==", + "license": "MIT", + "optional": true, + "dependencies": { + "execa": "^0.10.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "tslib": "^2.1.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/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/selderee": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", + "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", + "license": "MIT", + "optional": true, + "dependencies": { + "parseley": "^0.12.0" + }, + "funding": { + "url": "https://ko-fi.com/killymxi" + } + }, + "node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slick": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", + "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", + "license": "MIT (http://mootools.net/license.txt)", + "optional": true, + "engines": { + "node": "*" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/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/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/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/socket.io/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/socket.io/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/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strnum": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/strtok3": { + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser": { + "version": "5.44.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", + "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tlds": { + "version": "1.261.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.261.0.tgz", + "integrity": "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==", + "license": "MIT", + "optional": true, + "bin": { + "tlds": "bin.js" + } + }, + "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==", + "devOptional": 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/token-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz", + "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==", + "license": "MIT", + "optional": true + }, + "node_modules/token-types": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", + "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.1.0", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT", + "optional": true + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-loader": { + "version": "9.5.4", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.4.tgz", + "integrity": "sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tsconfig-paths-webpack-plugin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", + "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.7.0", + "tapable": "^2.2.1", + "tsconfig-paths": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.46.3", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.3.tgz", + "integrity": "sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.46.3", + "@typescript-eslint/parser": "8.46.3", + "@typescript-eslint/typescript-estree": "8.46.3", + "@typescript-eslint/utils": "8.46.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT", + "optional": true + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "license": "MIT", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "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/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", + "license": "MIT", + "optional": true + }, + "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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/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/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/valid-data-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", + "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/validator": { + "version": "13.15.20", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.20.tgz", + "integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "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/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-resource-inliner": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", + "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "escape-goat": "^3.0.0", + "htmlparser2": "^5.0.0", + "mime": "^2.4.6", + "node-fetch": "^2.6.0", + "valid-data-url": "^3.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/web-resource-inliner/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "license": "MIT", + "optional": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/dom-serializer/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/domutils/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "license": "BSD-2-Clause", + "optional": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/fb55/htmlparser2?sponsor=1" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/webpack": { + "version": "5.102.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz", + "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.26.3", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.3", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.4", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-node-externals": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", + "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/webpack-sources": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/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/webpack/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/webpack/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "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/with": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/parser": "^7.9.6", + "@babel/types": "^7.9.6", + "assert-never": "^1.2.1", + "babel-walk": "3.0.0-canary-5" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "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" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "optional": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/Smartes-Klassenzimmer-Backend/package.json b/Smartes-Klassenzimmer-Backend/package.json new file mode 100644 index 0000000..ada212e --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/package.json @@ -0,0 +1,74 @@ +{ + "name": "Smartes-Klassenzimmer-Backend", + "version": "0.0.1", + "description": "", + "author": "", + "private": true, + "license": "UNLICENSED", + "scripts": { + "build": "nest build", + "format": "prettier --write \"src/**/*.ts\"", + "start": "nest start", + "start:dev": "nest start --watch", + "start:debug": "nest start --debug --watch", + "start:prod": "node dist/main", + "lint": "eslint \"src/**/*.ts\" --fix", + "seed": "ts-node prisma/seed.ts" + }, + "prisma": { + "seed": "ts-node prisma/seed.ts" + }, + "dependencies": { + "@nestjs-modules/mailer": "^2.0.2", + "@nestjs/common": "^11.1.8", + "@nestjs/config": "^4.0.2", + "@nestjs/core": "^11.0.1", + "@nestjs/jwt": "^11.0.1", + "@nestjs/mapped-types": "*", + "@nestjs/passport": "^11.0.5", + "@nestjs/platform-express": "^11.1.8", + "@nestjs/platform-socket.io": "^11.1.8", + "@nestjs/schedule": "^6.1.0", + "@nestjs/websockets": "^11.1.8", + "@prisma/client": "^6.19.0", + "@types/cron": "^2.0.1", + "@types/multer": "^2.0.0", + "bcrypt": "^6.0.0", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.2", + "cookie-parser": "^1.4.7", + "dotenv": "^17.2.3", + "handlebars": "^4.7.8", + "multer": "^2.0.2", + "nodemailer": "^7.0.11", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1", + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "socket.io": "^4.8.1" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "^9.18.0", + "@nestjs/cli": "^11.0.0", + "@nestjs/schematics": "^11.0.0", + "@types/bcrypt": "^6.0.0", + "@types/cookie-parser": "^1.4.10", + "@types/express": "^5.0.0", + "@types/node": "^22.10.7", + "@types/nodemailer": "^7.0.4", + "@types/passport-jwt": "^4.0.1", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2", + "globals": "^16.0.0", + "prettier": "^3.4.2", + "prisma": "^6.19.0", + "source-map-support": "^0.5.21", + "ts-loader": "^9.5.2", + "ts-node": "^10.9.2", + "tsconfig-paths": "^4.2.0", + "typescript": "^5.7.3", + "typescript-eslint": "^8.20.0" + } +} diff --git a/Smartes-Klassenzimmer-Backend/prisma.config.ts b/Smartes-Klassenzimmer-Backend/prisma.config.ts new file mode 100644 index 0000000..2592d12 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/prisma.config.ts @@ -0,0 +1,13 @@ +import "dotenv/config"; +import { defineConfig, env } from "prisma/config"; + +export default defineConfig({ + schema: "prisma/schema.prisma", + migrations: { + path: "prisma/migrations", + }, + engine: "classic", + datasource: { + url: env("DATABASE_URL"), + }, +}); diff --git a/Smartes-Klassenzimmer-Backend/prisma/migrations/20251206130343_normalize_schema/migration.sql b/Smartes-Klassenzimmer-Backend/prisma/migrations/20251206130343_normalize_schema/migration.sql new file mode 100644 index 0000000..1725163 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/prisma/migrations/20251206130343_normalize_schema/migration.sql @@ -0,0 +1,255 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "email" TEXT NOT NULL, + "username" TEXT NOT NULL, + "password" TEXT NOT NULL, + "role" TEXT NOT NULL DEFAULT 'Student', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Subject" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "abbreviation" TEXT, + "color" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Subject_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Room" ( + "id" SERIAL NOT NULL, + "number" TEXT NOT NULL, + "building" TEXT, + "capacity" INTEGER, + "equipment" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Room_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "TimetableEntry" ( + "id" SERIAL NOT NULL, + "dayOfWeek" INTEGER NOT NULL, + "startTime" TEXT NOT NULL, + "endTime" TEXT NOT NULL, + "subjectId" INTEGER NOT NULL, + "teacherId" INTEGER, + "roomId" INTEGER, + "weekNumber" INTEGER, + "year" INTEGER, + "isRecurring" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "TimetableEntry_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Feedback" ( + "id" SERIAL NOT NULL, + "studentId" INTEGER NOT NULL, + "teacherId" INTEGER NOT NULL, + "lessonId" INTEGER NOT NULL, + "lessonDate" TIMESTAMP(3) NOT NULL, + "overallRating" INTEGER NOT NULL, + "categories" JSONB, + "whatWasGood" TEXT, + "whatCanImprove" TEXT, + "additionalComments" TEXT, + "isAnonymous" BOOLEAN NOT NULL DEFAULT false, + "allowTeacherResponse" BOOLEAN NOT NULL DEFAULT true, + "teacherResponse" TEXT, + "teacherRespondedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Feedback_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Grade" ( + "id" SERIAL NOT NULL, + "studentId" INTEGER NOT NULL, + "timetableEntryId" INTEGER NOT NULL, + "weekNumber" INTEGER NOT NULL, + "year" INTEGER NOT NULL, + "grade" DOUBLE PRECISION NOT NULL, + "gradeType" TEXT NOT NULL, + "weight" DOUBLE PRECISION DEFAULT 1.0, + "teacherId" INTEGER NOT NULL, + "title" TEXT, + "description" TEXT, + "maxPoints" DOUBLE PRECISION, + "achievedPoints" DOUBLE PRECISION, + "date" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Grade_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ChatGroup" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdById" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ChatGroup_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ChatGroupMember" ( + "id" SERIAL NOT NULL, + "userId" INTEGER NOT NULL, + "groupId" INTEGER NOT NULL, + "joinedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "ChatGroupMember_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ChatMessage" ( + "id" SERIAL NOT NULL, + "content" TEXT NOT NULL, + "userId" INTEGER NOT NULL, + "groupId" INTEGER NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "ChatMessage_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); + +-- CreateIndex +CREATE UNIQUE INDEX "Subject_name_key" ON "Subject"("name"); + +-- CreateIndex +CREATE INDEX "Subject_name_idx" ON "Subject"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "Room_number_key" ON "Room"("number"); + +-- CreateIndex +CREATE INDEX "Room_number_idx" ON "Room"("number"); + +-- CreateIndex +CREATE INDEX "TimetableEntry_dayOfWeek_idx" ON "TimetableEntry"("dayOfWeek"); + +-- CreateIndex +CREATE INDEX "TimetableEntry_weekNumber_year_idx" ON "TimetableEntry"("weekNumber", "year"); + +-- CreateIndex +CREATE INDEX "TimetableEntry_subjectId_idx" ON "TimetableEntry"("subjectId"); + +-- CreateIndex +CREATE INDEX "TimetableEntry_teacherId_idx" ON "TimetableEntry"("teacherId"); + +-- CreateIndex +CREATE INDEX "TimetableEntry_roomId_idx" ON "TimetableEntry"("roomId"); + +-- CreateIndex +CREATE INDEX "Feedback_studentId_idx" ON "Feedback"("studentId"); + +-- CreateIndex +CREATE INDEX "Feedback_teacherId_idx" ON "Feedback"("teacherId"); + +-- CreateIndex +CREATE INDEX "Feedback_lessonId_idx" ON "Feedback"("lessonId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Feedback_studentId_lessonId_key" ON "Feedback"("studentId", "lessonId"); + +-- CreateIndex +CREATE INDEX "Grade_studentId_idx" ON "Grade"("studentId"); + +-- CreateIndex +CREATE INDEX "Grade_timetableEntryId_idx" ON "Grade"("timetableEntryId"); + +-- CreateIndex +CREATE INDEX "Grade_teacherId_idx" ON "Grade"("teacherId"); + +-- CreateIndex +CREATE INDEX "Grade_weekNumber_year_idx" ON "Grade"("weekNumber", "year"); + +-- CreateIndex +CREATE UNIQUE INDEX "Grade_studentId_timetableEntryId_weekNumber_year_gradeType_key" ON "Grade"("studentId", "timetableEntryId", "weekNumber", "year", "gradeType"); + +-- CreateIndex +CREATE INDEX "ChatGroup_createdById_idx" ON "ChatGroup"("createdById"); + +-- CreateIndex +CREATE INDEX "ChatGroupMember_userId_idx" ON "ChatGroupMember"("userId"); + +-- CreateIndex +CREATE INDEX "ChatGroupMember_groupId_idx" ON "ChatGroupMember"("groupId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ChatGroupMember_userId_groupId_key" ON "ChatGroupMember"("userId", "groupId"); + +-- CreateIndex +CREATE INDEX "ChatMessage_groupId_idx" ON "ChatMessage"("groupId"); + +-- CreateIndex +CREATE INDEX "ChatMessage_userId_idx" ON "ChatMessage"("userId"); + +-- CreateIndex +CREATE INDEX "ChatMessage_createdAt_idx" ON "ChatMessage"("createdAt"); + +-- AddForeignKey +ALTER TABLE "TimetableEntry" ADD CONSTRAINT "TimetableEntry_subjectId_fkey" FOREIGN KEY ("subjectId") REFERENCES "Subject"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "TimetableEntry" ADD CONSTRAINT "TimetableEntry_teacherId_fkey" FOREIGN KEY ("teacherId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "TimetableEntry" ADD CONSTRAINT "TimetableEntry_roomId_fkey" FOREIGN KEY ("roomId") REFERENCES "Room"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Feedback" ADD CONSTRAINT "Feedback_studentId_fkey" FOREIGN KEY ("studentId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Feedback" ADD CONSTRAINT "Feedback_teacherId_fkey" FOREIGN KEY ("teacherId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Feedback" ADD CONSTRAINT "Feedback_lessonId_fkey" FOREIGN KEY ("lessonId") REFERENCES "TimetableEntry"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Grade" ADD CONSTRAINT "Grade_studentId_fkey" FOREIGN KEY ("studentId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Grade" ADD CONSTRAINT "Grade_timetableEntryId_fkey" FOREIGN KEY ("timetableEntryId") REFERENCES "TimetableEntry"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Grade" ADD CONSTRAINT "Grade_teacherId_fkey" FOREIGN KEY ("teacherId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ChatGroup" ADD CONSTRAINT "ChatGroup_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ChatGroupMember" ADD CONSTRAINT "ChatGroupMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ChatGroupMember" ADD CONSTRAINT "ChatGroupMember_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "ChatGroup"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ChatMessage" ADD CONSTRAINT "ChatMessage_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ChatMessage" ADD CONSTRAINT "ChatMessage_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "ChatGroup"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/Smartes-Klassenzimmer-Backend/prisma/migrations/20251206143255_add_password_reset/migration.sql b/Smartes-Klassenzimmer-Backend/prisma/migrations/20251206143255_add_password_reset/migration.sql new file mode 100644 index 0000000..00c8612 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/prisma/migrations/20251206143255_add_password_reset/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "User" ADD COLUMN "passwordResetExpires" TIMESTAMP(3), +ADD COLUMN "passwordResetToken" TEXT; diff --git a/Smartes-Klassenzimmer-Backend/prisma/migrations/20251206151112_add_files/migration.sql b/Smartes-Klassenzimmer-Backend/prisma/migrations/20251206151112_add_files/migration.sql new file mode 100644 index 0000000..868a743 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/prisma/migrations/20251206151112_add_files/migration.sql @@ -0,0 +1,28 @@ +-- AlterTable +ALTER TABLE "TimetableEntry" ADD COLUMN "allowStudentUploads" BOOLEAN NOT NULL DEFAULT false; + +-- CreateTable +CREATE TABLE "File" ( + "id" SERIAL NOT NULL, + "filename" TEXT NOT NULL, + "path" TEXT NOT NULL, + "mimetype" TEXT NOT NULL, + "size" INTEGER NOT NULL, + "uploadedById" INTEGER NOT NULL, + "timetableEntryId" INTEGER, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "File_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "File_uploadedById_idx" ON "File"("uploadedById"); + +-- CreateIndex +CREATE INDEX "File_timetableEntryId_idx" ON "File"("timetableEntryId"); + +-- AddForeignKey +ALTER TABLE "File" ADD CONSTRAINT "File_uploadedById_fkey" FOREIGN KEY ("uploadedById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "File" ADD CONSTRAINT "File_timetableEntryId_fkey" FOREIGN KEY ("timetableEntryId") REFERENCES "TimetableEntry"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/Smartes-Klassenzimmer-Backend/prisma/migrations/20251210185231_add_students_and_attendance/migration.sql b/Smartes-Klassenzimmer-Backend/prisma/migrations/20251210185231_add_students_and_attendance/migration.sql new file mode 100644 index 0000000..c1f065d --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/prisma/migrations/20251210185231_add_students_and_attendance/migration.sql @@ -0,0 +1,32 @@ +-- CreateTable +CREATE TABLE "students" ( + "student_id" SERIAL NOT NULL, + "student_first_name" TEXT NOT NULL, + "student_last_name" TEXT NOT NULL, + "student_class_name" TEXT, + "student_rfid_card_uid" TEXT NOT NULL, + "student_card_is_active" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "students_pkey" PRIMARY KEY ("student_id") +); + +-- CreateTable +CREATE TABLE "attendance_logs" ( + "attendance_log_id" SERIAL NOT NULL, + "student_id" INTEGER NOT NULL, + "attendance_scanned_at" TIMESTAMP(3) NOT NULL, + "attendance_event_type" TEXT NOT NULL, + "was_manual_entry" BOOLEAN NOT NULL DEFAULT false, + "manual_entry_reason" TEXT, + + CONSTRAINT "attendance_logs_pkey" PRIMARY KEY ("attendance_log_id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "students_student_rfid_card_uid_key" ON "students"("student_rfid_card_uid"); + +-- CreateIndex +CREATE INDEX "attendance_logs_student_id_attendance_scanned_at_idx" ON "attendance_logs"("student_id", "attendance_scanned_at"); + +-- AddForeignKey +ALTER TABLE "attendance_logs" ADD CONSTRAINT "attendance_logs_student_id_fkey" FOREIGN KEY ("student_id") REFERENCES "students"("student_id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/Smartes-Klassenzimmer-Backend/prisma/migrations/migration_lock.toml b/Smartes-Klassenzimmer-Backend/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/Smartes-Klassenzimmer-Backend/prisma/schema.prisma b/Smartes-Klassenzimmer-Backend/prisma/schema.prisma new file mode 100644 index 0000000..774cf6d --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/prisma/schema.prisma @@ -0,0 +1,279 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +generator client { + provider = "prisma-client-js" + output = "../node_modules/.prisma/client" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id Int @id @default(autoincrement()) + email String @unique + username String @unique + password String + role String @default("Student") + + // Password Reset + passwordResetToken String? + passwordResetExpires DateTime? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + receivedGrades Grade[] @relation("ReceivedGrades") // Noten, die Schüler erhalten haben + assignedGrades Grade[] @relation("AssignedGrades") // Noten, die Lehrer vergeben haben + chatMessages ChatMessage[] + chatGroups ChatGroupMember[] + studentFeedbacks Feedback[] @relation("StudentFeedbacks") + teacherFeedbacks Feedback[] @relation("TeacherFeedbacks") + taughtTimetableEntries TimetableEntry[] @relation("TeacherTimetableEntries") + createdChatGroups ChatGroup[] @relation("ChatGroupCreator") + uploadedFiles File[] +} + +// Neue Tabelle: File (Dateien) +model File { + id Int @id @default(autoincrement()) + filename String + path String + mimetype String + size Int + + uploadedById Int + uploadedBy User @relation(fields: [uploadedById], references: [id], onDelete: Cascade) + + timetableEntryId Int? + timetableEntry TimetableEntry? @relation(fields: [timetableEntryId], references: [id], onDelete: Cascade) + + createdAt DateTime @default(now()) + + @@index([uploadedById]) + @@index([timetableEntryId]) +} + +// Neue Tabelle: Subject (Fächer) +model Subject { + id Int @id @default(autoincrement()) + name String @unique // z.B. "Mathematik", "Deutsch", "Englisch" + abbreviation String? // z.B. "Mathe", "DE", "EN" + color String? // Farbe für UI (z.B. "#FF5733") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + timetableEntries TimetableEntry[] + + @@index([name]) +} + +// Neue Tabelle: Room (Räume) +model Room { + id Int @id @default(autoincrement()) + number String @unique // z.B. "A101", "B203" + building String? // z.B. "Hauptgebäude", "Neubau" + capacity Int? // Anzahl Sitzplätze + equipment String? // z.B. "Beamer, Whiteboard" + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + timetableEntries TimetableEntry[] + + @@index([number]) +} + +model TimetableEntry { + id Int @id @default(autoincrement()) + dayOfWeek Int // 1 = Montag, 2 = Dienstag, ..., 5 = Freitag + startTime String // Format: "08:00" + endTime String // Format: "09:30" + + // Normalisierte Relationen + subjectId Int + subject Subject @relation(fields: [subjectId], references: [id], onDelete: Restrict) + + teacherId Int? + teacher User? @relation("TeacherTimetableEntries", fields: [teacherId], references: [id], onDelete: SetNull) + + roomId Int? + room Room? @relation(fields: [roomId], references: [id], onDelete: SetNull) + + // Wochenbasierte Planung + weekNumber Int? // Kalenderwoche (1-53), null = alle Wochen + year Int? // Jahr (z.B. 2025), null = alle Jahre + isRecurring Boolean @default(true) // true = wiederholt sich jede Woche, false = nur für spezifische Woche + + allowStudentUploads Boolean @default(false) + files File[] + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + feedbacks Feedback[] + grades Grade[] + + @@index([dayOfWeek]) + @@index([weekNumber, year]) + @@index([subjectId]) + @@index([teacherId]) + @@index([roomId]) +} + +model Feedback { + id Int @id @default(autoincrement()) + + // Foreign Key Relations zu User-Tabelle + studentId Int + student User @relation("StudentFeedbacks", fields: [studentId], references: [id], onDelete: Cascade) + + teacherId Int + teacher User @relation("TeacherFeedbacks", fields: [teacherId], references: [id], onDelete: Cascade) + + // Foreign Key Relation zu TimetableEntry + lessonId Int + timetableEntry TimetableEntry @relation(fields: [lessonId], references: [id], onDelete: Cascade) + lessonDate DateTime + + overallRating Int + + // Prisma Json-Typ für Kategorien + categories Json? + + whatWasGood String? + whatCanImprove String? + additionalComments String? + + isAnonymous Boolean @default(false) + allowTeacherResponse Boolean @default(true) + + teacherResponse String? + teacherRespondedAt DateTime? + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // Unique Constraint: Ein Schüler kann nur einmal pro Stunde Feedback geben + @@unique([studentId, lessonId]) + @@index([studentId]) + @@index([teacherId]) + @@index([lessonId]) +} + +model Grade { + id Int @id @default(autoincrement()) + + // Schüler-Information (erhält die Note) + studentId Int + student User @relation("ReceivedGrades", fields: [studentId], references: [id], onDelete: Cascade) + + // Stundenplan-Bezug + timetableEntryId Int + timetableEntry TimetableEntry @relation(fields: [timetableEntryId], references: [id], onDelete: Cascade) + + // Wochenbezug (Note gilt für eine spezifische Woche) + weekNumber Int // Kalenderwoche (1-53) + year Int // Jahr (z.B. 2025) + + // Noten-Details + grade Float // z.B. 1.0, 2.5, 3.0 (deutsches Notensystem) + gradeType String // z.B. "Klausur", "Mitarbeit", "Hausaufgabe", "Mündlich" + weight Float? @default(1.0) // Gewichtung der Note (z.B. Klausur = 2.0, Mitarbeit = 1.0) + + // Lehrer-Information (vergibt die Note) + teacherId Int + teacher User @relation("AssignedGrades", fields: [teacherId], references: [id], onDelete: Restrict) + + // Optional: Detailbeschreibung + title String? // z.B. "Mathearbeit Kapitel 3" + description String? // Zusätzliche Informationen + maxPoints Float? // Maximale Punktzahl + achievedPoints Float? // Erreichte Punktzahl + + // Datum + date DateTime // Datum der Leistung + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([studentId, timetableEntryId, weekNumber, year, gradeType]) + @@index([studentId]) + @@index([timetableEntryId]) + @@index([teacherId]) + @@index([weekNumber, year]) +} + +model ChatGroup { + id Int @id @default(autoincrement()) + name String + description String? + + createdById Int // Lehrer, der die Gruppe erstellt hat + createdBy User @relation("ChatGroupCreator", fields: [createdById], references: [id], onDelete: Restrict) + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + members ChatGroupMember[] + messages ChatMessage[] + + @@index([createdById]) +} + +model ChatGroupMember { + id Int @id @default(autoincrement()) + + userId Int + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + groupId Int + group ChatGroup @relation(fields: [groupId], references: [id], onDelete: Cascade) + + joinedAt DateTime @default(now()) + + @@unique([userId, groupId]) + @@index([userId]) + @@index([groupId]) +} + +model ChatMessage { + id Int @id @default(autoincrement()) + content String + + userId Int + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + groupId Int + group ChatGroup @relation(fields: [groupId], references: [id], onDelete: Cascade) + + createdAt DateTime @default(now()) + + @@index([groupId]) + @@index([userId]) + @@index([createdAt]) +} + +model Student { + studentId Int @id @default(autoincrement()) @map("student_id") + studentFirstName String @map("student_first_name") + studentLastName String @map("student_last_name") + studentClassName String? @map("student_class_name") + studentRfidCardUid String @unique @map("student_rfid_card_uid") + studentCardIsActive Boolean @default(true) @map("student_card_is_active") + attendanceLogs AttendanceLog[] + + @@map("students") +} + +model AttendanceLog { + attendanceLogId Int @id @default(autoincrement()) @map("attendance_log_id") + studentId Int @map("student_id") + student Student @relation(fields: [studentId], references: [studentId], onDelete: Cascade) + attendanceScannedAt DateTime @map("attendance_scanned_at") + attendanceEventType String @map("attendance_event_type") + wasManualEntry Boolean @default(false) @map("was_manual_entry") + manualEntryReason String? @map("manual_entry_reason") + + @@index([studentId, attendanceScannedAt]) + @@map("attendance_logs") +} diff --git a/Smartes-Klassenzimmer-Backend/prisma/seed.ts b/Smartes-Klassenzimmer-Backend/prisma/seed.ts new file mode 100644 index 0000000..98af38d --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/prisma/seed.ts @@ -0,0 +1,236 @@ +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient(); + +async function main() { + console.log('🌱 Datenbank wird mit Testdaten befüllt...\n'); + + // 1. Benutzer erstellen (Lehrer und Schüler) + console.log('📝 Erstelle Benutzer...'); + const admin = await prisma.user.upsert({ + where: { email: 'admin@smartesklassenzimmer.de' }, + update: {}, + create: { + email: 'admin@smartesklassenzimmer.de', + username: 'admin', + password: '$2b$10$8I9F3DPBvqzm.tBz7yMgfur4z1gMyaHsPyhmL4VAGJ2OvnmTTn5KW', // admin123 + role: 'Lehrer', + }, + }); + + const teacher1 = await prisma.user.upsert({ + where: { email: 'mueller@schule.de' }, + update: {}, + create: { + email: 'mueller@schule.de', + username: 'Frau Müller', + password: '$2b$10$8I9F3DPBvqzm.tBz7yMgfur4z1gMyaHsPyhmL4VAGJ2OvnmTTn5KW', // admin123 + role: 'Lehrer', + }, + }); + + const teacher2 = await prisma.user.upsert({ + where: { email: 'schmidt@schule.de' }, + update: {}, + create: { + email: 'schmidt@schule.de', + username: 'Herr Schmidt', + password: '$2b$10$8I9F3DPBvqzm.tBz7yMgfur4z1gMyaHsPyhmL4VAGJ2OvnmTTn5KW', // admin123 + role: 'Lehrer', + }, + }); + + const student1 = await prisma.user.upsert({ + where: { email: 'max@student.de' }, + update: {}, + create: { + email: 'max@student.de', + username: 'Max Mustermann', + password: '$2b$10$8I9F3DPBvqzm.tBz7yMgfur4z1gMyaHsPyhmL4VAGJ2OvnmTTn5KW', // admin123 + role: 'Student', + }, + }); + + const student2 = await prisma.user.upsert({ + where: { email: 'anna@student.de' }, + update: {}, + create: { + email: 'anna@student.de', + username: 'Anna Schmidt', + password: '$2b$10$8I9F3DPBvqzm.tBz7yMgfur4z1gMyaHsPyhmL4VAGJ2OvnmTTn5KW', // admin123 + role: 'Student', + }, + }); + + console.log('✅ Benutzer erstellt\n'); + + // 2. Fächer erstellen + console.log('📚 Erstelle Fächer...'); + const mathSubject = await prisma.subject.upsert({ + where: { name: 'Mathematik' }, + update: {}, + create: { + name: 'Mathematik', + abbreviation: 'Mathe', + color: '#FF5733', + }, + }); + + const germanSubject = await prisma.subject.upsert({ + where: { name: 'Deutsch' }, + update: {}, + create: { + name: 'Deutsch', + abbreviation: 'DE', + color: '#33FF57', + }, + }); + + const englishSubject = await prisma.subject.upsert({ + where: { name: 'Englisch' }, + update: {}, + create: { + name: 'Englisch', + abbreviation: 'EN', + color: '#3357FF', + }, + }); + + const physicsSubject = await prisma.subject.upsert({ + where: { name: 'Physik' }, + update: {}, + create: { + name: 'Physik', + abbreviation: 'Phy', + color: '#FF33F5', + }, + }); + + console.log('✅ Fächer erstellt\n'); + + // 3. Räume erstellen + console.log('🏫 Erstelle Räume...'); + const roomA101 = await prisma.room.upsert({ + where: { number: 'A101' }, + update: {}, + create: { + number: 'A101', + building: 'Hauptgebäude', + capacity: 30, + equipment: 'Beamer, Whiteboard', + }, + }); + + const roomB203 = await prisma.room.upsert({ + where: { number: 'B203' }, + update: {}, + create: { + number: 'B203', + building: 'Neubau', + capacity: 25, + equipment: 'Smartboard, Laptop', + }, + }); + + const roomC105 = await prisma.room.upsert({ + where: { number: 'C105' }, + update: {}, + create: { + number: 'C105', + building: 'Hauptgebäude', + capacity: 20, + equipment: 'Beamer', + }, + }); + + console.log('✅ Räume erstellt\n'); + + // 4. Stundenplan-Einträge erstellen + console.log('📅 Erstelle Stundenplan-Einträge...'); + + // Montag + await prisma.timetableEntry.create({ + data: { + dayOfWeek: 1, + startTime: '08:00', + endTime: '09:30', + subjectId: mathSubject.id, + teacherId: teacher1.id, + roomId: roomA101.id, + isRecurring: true, + }, + }); + + await prisma.timetableEntry.create({ + data: { + dayOfWeek: 1, + startTime: '10:00', + endTime: '11:30', + subjectId: germanSubject.id, + teacherId: teacher2.id, + roomId: roomB203.id, + isRecurring: true, + }, + }); + + // Dienstag + await prisma.timetableEntry.create({ + data: { + dayOfWeek: 2, + startTime: '08:00', + endTime: '09:30', + subjectId: englishSubject.id, + teacherId: teacher1.id, + roomId: roomC105.id, + isRecurring: true, + }, + }); + + await prisma.timetableEntry.create({ + data: { + dayOfWeek: 2, + startTime: '10:00', + endTime: '11:30', + subjectId: physicsSubject.id, + teacherId: teacher2.id, + roomId: roomA101.id, + isRecurring: true, + }, + }); + + // Mittwoch + await prisma.timetableEntry.create({ + data: { + dayOfWeek: 3, + startTime: '08:00', + endTime: '09:30', + subjectId: mathSubject.id, + teacherId: teacher1.id, + roomId: roomA101.id, + isRecurring: true, + }, + }); + + console.log('✅ Stundenplan-Einträge erstellt\n'); + + console.log('\n=== LOGIN-DATEN ==='); + console.log('👨‍🏫 Lehrer:'); + console.log(' - Benutzername: admin, Passwort: admin123'); + console.log(' - Benutzername: Frau Müller, Passwort: admin123'); + console.log(' - Benutzername: Herr Schmidt, Passwort: admin123'); + console.log('\n👨‍🎓 Schüler:'); + console.log(' - Benutzername: Max Mustermann, Passwort: admin123'); + console.log(' - Benutzername: Anna Schmidt, Passwort: admin123'); + console.log('==================\n'); + + console.log('🎉 Datenbank erfolgreich mit Testdaten befüllt!\n'); +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/Smartes-Klassenzimmer-Backend/src/app.controller.ts b/Smartes-Klassenzimmer-Backend/src/app.controller.ts new file mode 100644 index 0000000..c3f5fef --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/app.controller.ts @@ -0,0 +1,12 @@ +import { Controller, Get } from '@nestjs/common'; +import { AppService } from './app.service'; + +@Controller('api') +export class AppController { + constructor(private readonly appService: AppService) {} + + @Get('health') + healthCheck() { + return this.appService.healthCheck(); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/app.module.ts b/Smartes-Klassenzimmer-Backend/src/app.module.ts new file mode 100644 index 0000000..4b3c389 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/app.module.ts @@ -0,0 +1,44 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { ScheduleModule } from '@nestjs/schedule'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { ChatModule } from './chat/chat.module'; +import { AuthModule } from './auth/auth.module'; +import { PrismaModule } from './prisma/prisma.module'; +import { FeedbackModule } from './feedback/feedback.module'; +import { UsersModule } from './users/users.module'; +import { TimetableModule } from './timetable/timetable.module'; +import { WhiteboardModule } from './whiteboard/whiteboard.module'; +import { GradesModule } from './grades/grades.module'; +import { SubjectsModule } from './subjects/subjects.module'; +import { RoomsModule } from './rooms/rooms.module'; +import { ClassroomModule } from './classroom/classroom.module'; +import { MailModule } from './mail/mail.module'; +import { FilesModule } from './files/files.module'; +import { IotModule } from './iot/iot.module'; + +@Module({ + imports: [ + ConfigModule.forRoot({ + isGlobal: true, + }), + ScheduleModule.forRoot(), + MailModule, + PrismaModule, + AuthModule, + ChatModule, + FeedbackModule, + UsersModule, + TimetableModule, + WhiteboardModule, + GradesModule, + SubjectsModule, + RoomsModule, + ClassroomModule, FilesModule, + IotModule + ], + controllers: [AppController], + providers: [AppService], +}) +export class AppModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/app.service.ts b/Smartes-Klassenzimmer-Backend/src/app.service.ts new file mode 100644 index 0000000..400a2b6 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/app.service.ts @@ -0,0 +1,12 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class AppService { + healthCheck() { + return { + status: 'ok', + timestamp: new Date().toISOString(), + service: 'Smartes Klassenzimmer API', + }; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/auth.controller.ts b/Smartes-Klassenzimmer-Backend/src/auth/auth.controller.ts new file mode 100644 index 0000000..32ad864 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/auth.controller.ts @@ -0,0 +1,88 @@ +import { + Controller, + Post, + Body, + Res, + HttpCode, + HttpStatus, + Get, + UseGuards, + Req, +} from '@nestjs/common'; +import type { Response, Request } from 'express'; +import { AuthService } from './auth.service'; +import { RegisterDto, LoginDto, ForgotPasswordDto, ResetPasswordDto } from './dto'; +import { JwtAuthGuard } from './guards'; + +@Controller('auth') +export class AuthController { + constructor(private readonly authService: AuthService) {} + + @Post('register') + async register( + @Body() registerDto: RegisterDto, + @Res({ passthrough: true }) res: Response, + ) { + const result = await this.authService.register(registerDto); + + // JWT als httpOnly Cookie setzen + res.cookie('access_token', result.token, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', // nur über HTTPS in Produktion + sameSite: process.env.NODE_ENV === 'production' ? 'strict' : 'lax', // lax für Cross-Origin in Dev + maxAge: 7 * 24 * 60 * 60 * 1000, // 7 Tage + }); + + return { + message: result.message, + user: result.user, + }; + } + + @Post('login') + @HttpCode(HttpStatus.OK) + async login( + @Body() loginDto: LoginDto, + @Res({ passthrough: true }) res: Response, + ) { + const result = await this.authService.login(loginDto); + + // JWT als httpOnly Cookie setzen + res.cookie('access_token', result.token, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: process.env.NODE_ENV === 'production' ? 'strict' : 'lax', // lax für Cross-Origin in Dev + maxAge: 7 * 24 * 60 * 60 * 1000, // 7 Tage + }); + + return { + message: result.message, + user: result.user, + }; + } + + @Post('forgot-password') + @HttpCode(HttpStatus.OK) + async forgotPassword(@Body() forgotPasswordDto: ForgotPasswordDto) { + return this.authService.forgotPassword(forgotPasswordDto.email); + } + + @Post('reset-password') + @HttpCode(HttpStatus.OK) + async resetPassword(@Body() resetPasswordDto: ResetPasswordDto) { + return this.authService.resetPassword(resetPasswordDto); + } + + @Post('logout') + @HttpCode(HttpStatus.OK) + logout(@Res({ passthrough: true }) res: Response) { + res.clearCookie('access_token'); + return { message: 'Logout erfolgreich' }; + } + + @Get('me') + @UseGuards(JwtAuthGuard) + getProfile(@Req() req: Request) { + return req.user; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/auth.module.ts b/Smartes-Klassenzimmer-Backend/src/auth/auth.module.ts new file mode 100644 index 0000000..955e48c --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/auth.module.ts @@ -0,0 +1,26 @@ +import { Module } from '@nestjs/common'; +import { JwtModule } from '@nestjs/jwt'; +import { PassportModule } from '@nestjs/passport'; +import { AuthService } from './auth.service'; +import { AuthController } from './auth.controller'; +import { JwtStrategy } from './strategies/jwt.strategy'; +import { PrismaModule } from '../prisma/prisma.module'; +import { MailModule } from '../mail/mail.module'; + +@Module({ + imports: [ + PrismaModule, + MailModule, + PassportModule, + JwtModule.register({ + secret: process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production', + signOptions: { + expiresIn: '7d' + }, + }), + ], + controllers: [AuthController], + providers: [AuthService, JwtStrategy], + exports: [AuthService, JwtModule], +}) +export class AuthModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/auth.service.ts b/Smartes-Klassenzimmer-Backend/src/auth/auth.service.ts new file mode 100644 index 0000000..e598242 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/auth.service.ts @@ -0,0 +1,193 @@ +import { + Injectable, + UnauthorizedException, + ConflictException, + NotFoundException, + BadRequestException, +} from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import * as bcrypt from 'bcrypt'; +import * as crypto from 'crypto'; +import { PrismaService } from '../prisma/prisma.service'; +import { RegisterDto, LoginDto } from './dto'; +import { MailService } from '../mail/mail.service'; +import { ResetPasswordDto } from './dto/reset-password.dto'; + +@Injectable() +export class AuthService { + constructor( + private prisma: PrismaService, + private jwtService: JwtService, + private mailService: MailService, + ) {} + + async register(registerDto: RegisterDto) { + const { email, username, password, role } = registerDto; + + // Prüfen ob User bereits existiert + const existingUser = await this.prisma.user.findFirst({ + where: { + OR: [{ email }, { username }], + }, + }); + + if (existingUser) { + if (existingUser.email === email) { + throw new ConflictException('Email bereits registriert'); + } + if (existingUser.username === username) { + throw new ConflictException('Username bereits vergeben'); + } + } + + // Password hashen + const hashedPassword = await bcrypt.hash(password, 10); + + // User erstellen + const user = await this.prisma.user.create({ + data: { + email, + username, + password: hashedPassword, + role: role || 'Student', + }, + }); + + // Willkommens-Email senden + try { + await this.mailService.sendUserConfirmation(user); + } catch (error) { + // Log error but don't fail registration + console.error('Email sending failed:', error); + } + + // JWT Token generieren + const token = await this.generateToken(user.id, user.username, user.role, user.email); + + return { + message: 'Registrierung erfolgreich', + user: { + id: user.id, + email: user.email, + username: user.username, + role: user.role, + }, + token, + }; + } + + async login(loginDto: LoginDto) { + const { username, password } = loginDto; + + // User finden + const user = await this.prisma.user.findUnique({ + where: { username }, + }); + + if (!user) { + throw new UnauthorizedException('Ungültige Anmeldedaten'); + } + + // Password vergleichen + const isPasswordValid = await bcrypt.compare(password, user.password); + + if (!isPasswordValid) { + throw new UnauthorizedException('Ungültige Anmeldedaten'); + } + + // JWT Token generieren + const token = await this.generateToken(user.id, user.username, user.role, user.email); + + return { + message: 'Login erfolgreich', + user: { + id: user.id, + email: user.email, + username: user.username, + role: user.role, + }, + token, + }; + } + + async forgotPassword(email: string) { + const user = await this.prisma.user.findUnique({ + where: { email }, + }); + + if (user) { + const token = crypto.randomBytes(32).toString('hex'); + const expires = new Date(); + expires.setHours(expires.getHours() + 1); // 1 Stunde gültig + + await this.prisma.user.update({ + where: { id: user.id }, + data: { + passwordResetToken: token, + passwordResetExpires: expires, + }, + }); + + try { + await this.mailService.sendPasswordReset(user, token); + } catch (error) { + console.error('Email sending failed:', error); + } + } + + // Immer die gleiche Nachricht zurückgeben, egal ob User existiert oder nicht + return { message: 'Falls ein Konto mit dieser E-Mail existiert, wurde ein Link zum Zurücksetzen gesendet.' }; + } + + async resetPassword(resetPasswordDto: ResetPasswordDto) { + const { token, newPassword } = resetPasswordDto; + + const user = await this.prisma.user.findFirst({ + where: { + passwordResetToken: token, + passwordResetExpires: { + gt: new Date(), + }, + }, + }); + + if (!user) { + throw new BadRequestException('Ungültiger oder abgelaufener Token'); + } + + const hashedPassword = await bcrypt.hash(newPassword, 10); + + await this.prisma.user.update({ + where: { id: user.id }, + data: { + password: hashedPassword, + passwordResetToken: null, + passwordResetExpires: null, + }, + }); + + return { message: 'Passwort erfolgreich geändert' }; + } + + async validateUser(userId: number) { + const user = await this.prisma.user.findUnique({ + where: { id: userId }, + }); + + if (!user) { + throw new UnauthorizedException('User nicht gefunden'); + } + + return { + id: user.id, + email: user.email, + username: user.username, + role: user.role, + }; + } + + private async generateToken(userId: number, username: string, role: string, email?: string) { + const payload = { sub: userId, username, role, email }; + return this.jwtService.sign(payload); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/dto/forgot-password.dto.ts b/Smartes-Klassenzimmer-Backend/src/auth/dto/forgot-password.dto.ts new file mode 100644 index 0000000..9dca7c8 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/dto/forgot-password.dto.ts @@ -0,0 +1,7 @@ +import { IsEmail, IsNotEmpty } from 'class-validator'; + +export class ForgotPasswordDto { + @IsEmail() + @IsNotEmpty() + email: string; +} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/dto/index.ts b/Smartes-Klassenzimmer-Backend/src/auth/dto/index.ts new file mode 100644 index 0000000..432f0ec --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/dto/index.ts @@ -0,0 +1,4 @@ +export * from './login.dto'; +export * from './register.dto'; +export * from './forgot-password.dto'; +export * from './reset-password.dto'; \ No newline at end of file diff --git a/Smartes-Klassenzimmer-Backend/src/auth/dto/login.dto.ts b/Smartes-Klassenzimmer-Backend/src/auth/dto/login.dto.ts new file mode 100644 index 0000000..0af27f1 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/dto/login.dto.ts @@ -0,0 +1,11 @@ +import { IsNotEmpty, IsString } from 'class-validator'; + +export class LoginDto { + @IsString() + @IsNotEmpty() + username: string; + + @IsString() + @IsNotEmpty() + password: string; +} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/dto/register.dto.ts b/Smartes-Klassenzimmer-Backend/src/auth/dto/register.dto.ts new file mode 100644 index 0000000..31f15f6 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/dto/register.dto.ts @@ -0,0 +1,22 @@ +import { IsEmail, IsNotEmpty, IsString, MinLength, IsOptional, IsIn } from 'class-validator'; + +export class RegisterDto { + @IsEmail() + @IsNotEmpty() + email: string; + + @IsString() + @IsNotEmpty() + @MinLength(3) + username: string; + + @IsString() + @IsNotEmpty() + @MinLength(6) + password: string; + + @IsOptional() + @IsString() + @IsIn(['Student', 'Lehrer']) + role?: string = 'Student'; +} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/dto/reset-password.dto.ts b/Smartes-Klassenzimmer-Backend/src/auth/dto/reset-password.dto.ts new file mode 100644 index 0000000..9d5413b --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/dto/reset-password.dto.ts @@ -0,0 +1,12 @@ +import { IsNotEmpty, IsString, MinLength } from 'class-validator'; + +export class ResetPasswordDto { + @IsString() + @IsNotEmpty() + token: string; + + @IsString() + @IsNotEmpty() + @MinLength(6) + newPassword: string; +} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/guards/index.ts b/Smartes-Klassenzimmer-Backend/src/auth/guards/index.ts new file mode 100644 index 0000000..6c788f4 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/guards/index.ts @@ -0,0 +1,3 @@ +export * from './jwt-auth.guard'; +export * from './ws-jwt.guard'; +export * from './roles.guard'; diff --git a/Smartes-Klassenzimmer-Backend/src/auth/guards/jwt-auth.guard.ts b/Smartes-Klassenzimmer-Backend/src/auth/guards/jwt-auth.guard.ts new file mode 100644 index 0000000..2155290 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/guards/jwt-auth.guard.ts @@ -0,0 +1,5 @@ +import { Injectable } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Injectable() +export class JwtAuthGuard extends AuthGuard('jwt') {} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/guards/roles.guard.ts b/Smartes-Klassenzimmer-Backend/src/auth/guards/roles.guard.ts new file mode 100644 index 0000000..77d0f1d --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/guards/roles.guard.ts @@ -0,0 +1,31 @@ +import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; + +export const Roles = Reflector.createDecorator(); + +@Injectable() +export class RolesGuard implements CanActivate { + constructor(private reflector: Reflector) {} + + canActivate(context: ExecutionContext): boolean { + const requiredRoles = this.reflector.get(Roles, context.getHandler()); + if (!requiredRoles) { + return true; // No role required + } + + const request = context.switchToHttp().getRequest(); + const user = request.user; + + if (!user || !user.role) { + throw new ForbiddenException('Zugriff verweigert - Keine Rolle gesetzt'); + } + + if (!requiredRoles.includes(user.role)) { + throw new ForbiddenException( + `Zugriff verweigert - Erforderliche Rolle(n): ${requiredRoles.join(', ')}` + ); + } + + return true; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/guards/ws-jwt.guard.ts b/Smartes-Klassenzimmer-Backend/src/auth/guards/ws-jwt.guard.ts new file mode 100644 index 0000000..f8ade86 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/guards/ws-jwt.guard.ts @@ -0,0 +1,58 @@ +import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import { WsException } from '@nestjs/websockets'; +import { Socket } from 'socket.io'; + +@Injectable() +export class WsJwtGuard implements CanActivate { + constructor(private jwtService: JwtService) {} + + canActivate(context: ExecutionContext): boolean { + try { + const client: Socket = context.switchToWs().getClient(); + const token = this.extractTokenFromHandshake(client); + + if (!token) { + throw new WsException('Unauthorized: No token provided'); + } + + const payload = this.jwtService.verify(token, { + secret: process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production', + }); + + // User-Daten zum Socket hinzufügen + client.data.user = payload; + return true; + } catch (error) { + throw new WsException('Unauthorized: Invalid token'); + } + } + + private extractTokenFromHandshake(client: Socket): string | null { + // Token aus Cookie extrahieren + const cookies = client.handshake.headers.cookie; + if (cookies) { + const cookieArray = cookies.split(';'); + for (const cookie of cookieArray) { + const [name, value] = cookie.trim().split('='); + if (name === 'access_token') { + return value; + } + } + } + + // Fallback: Token aus auth object (wenn vom Client übergeben) + const token = client.handshake.auth?.token; + if (token) { + return token; + } + + // Fallback: Token aus query parameter + const queryToken = client.handshake.query?.token as string; + if (queryToken) { + return queryToken; + } + + return null; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/auth/strategies/jwt.strategy.ts b/Smartes-Klassenzimmer-Backend/src/auth/strategies/jwt.strategy.ts new file mode 100644 index 0000000..0c18316 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/auth/strategies/jwt.strategy.ts @@ -0,0 +1,37 @@ +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { Request } from 'express'; +import { AuthService } from '../auth.service'; + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor(private authService: AuthService) { + super({ + jwtFromRequest: ExtractJwt.fromExtractors([ + (request: Request) => { + // JWT aus Cookie extrahieren + return request?.cookies?.access_token; + }, + ExtractJwt.fromAuthHeaderAsBearerToken(), // Fallback: Authorization Header + ]), + ignoreExpiration: false, + secretOrKey: process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production', + }); + } + + async validate(payload: any) { + const user = await this.authService.validateUser(payload.sub); + if (!user) { + throw new UnauthorizedException(); + } + // Rückgabe des Payloads mit User-Daten + return { + id: payload.sub, + sub: payload.sub, + username: payload.username, + email: payload.email, + role: payload.role, + }; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/chat/chat.controller.ts b/Smartes-Klassenzimmer-Backend/src/chat/chat.controller.ts new file mode 100644 index 0000000..829885c --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/chat/chat.controller.ts @@ -0,0 +1,96 @@ +import { + Controller, + Get, + Post, + Put, + Delete, + Body, + Param, + Query, + UseGuards, + Request, + ParseIntPipe, +} from '@nestjs/common'; +import { ChatService } from './chat.service'; +import { CreateGroupDto } from './dto/create-group.dto'; +import { UpdateGroupDto } from './dto/update-group.dto'; +import { AddMembersDto } from './dto/add-members.dto'; +import { JwtAuthGuard } from '../auth/guards'; + +@Controller('live') +@UseGuards(JwtAuthGuard) +export class ChatController { + constructor(private readonly chatService: ChatService) {} + + @Get() + getChatInfo(): object { + return { + message: 'Chat API is running', + websocketPath: '/api/live', + }; + } + + // Gruppen-Verwaltung + @Post('groups') + createGroup(@Request() req, @Body() dto: CreateGroupDto) { + return this.chatService.createGroup(req.user.sub, dto); + } + + @Get('groups') + getAllGroups() { + return this.chatService.getAllGroups(); + } + + @Get('groups/my') + getUserGroups(@Request() req) { + return this.chatService.getUserGroups(req.user.sub); + } + + @Get('groups/:id') + getGroupById(@Param('id', ParseIntPipe) id: number) { + return this.chatService.getGroupById(id); + } + + @Put('groups/:id') + updateGroup( + @Param('id', ParseIntPipe) id: number, + @Request() req, + @Body() dto: UpdateGroupDto, + ) { + return this.chatService.updateGroup(id, req.user.sub, req.user.role, dto); + } + + @Delete('groups/:id') + deleteGroup(@Param('id', ParseIntPipe) id: number, @Request() req) { + return this.chatService.deleteGroup(id, req.user.sub, req.user.role); + } + + @Post('groups/:id/members') + addMembers( + @Param('id', ParseIntPipe) id: number, + @Request() req, + @Body() dto: AddMembersDto, + ) { + return this.chatService.addMembers(id, req.user.sub, req.user.role, dto); + } + + @Delete('groups/:id/members/:userId') + removeMember( + @Param('id', ParseIntPipe) id: number, + @Param('userId', ParseIntPipe) userId: number, + @Request() req, + ) { + return this.chatService.removeMember(id, userId, req.user.sub, req.user.role); + } + + // Nachrichten + @Get('groups/:id/messages') + getGroupMessages( + @Param('id', ParseIntPipe) id: number, + @Request() req, + @Query('limit') limit?: number, + @Query('offset') offset?: number, + ) { + return this.chatService.getGroupMessages(id, req.user.sub, limit, offset); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/chat/chat.gateway.ts b/Smartes-Klassenzimmer-Backend/src/chat/chat.gateway.ts new file mode 100644 index 0000000..e01d2e8 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/chat/chat.gateway.ts @@ -0,0 +1,233 @@ +import { + WebSocketGateway, + WebSocketServer, + SubscribeMessage, + MessageBody, + ConnectedSocket, + OnGatewayConnection, + OnGatewayDisconnect, +} from '@nestjs/websockets'; +import { UseGuards } from '@nestjs/common'; +import { Server, Socket } from 'socket.io'; +import { JwtService } from '@nestjs/jwt'; +import { WsJwtGuard } from '../auth/guards'; +import { ChatService } from './chat.service'; + +@WebSocketGateway({ + namespace: '/api/live', + cors: { + origin: ['http://localhost:5173', 'http://localhost:3000'], + credentials: true, + }, +}) +export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect { + @WebSocketServer() + server: Server; + + constructor( + private readonly jwtService: JwtService, + private readonly chatService: ChatService, + ) {} + + // ... (handleConnection stays same) + + handleConnection(client: Socket) { + try { + const token = this.extractTokenFromHandshake(client); + + if (!token) { + console.log(`Client ${client.id} rejected: No token provided`); + client.emit('error', { message: 'Unauthorized: No token provided' }); + client.disconnect(); + return; + } + + const payload = this.jwtService.verify(token, { + secret: process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production', + }); + + // User-Daten zum Socket hinzufügen + client.data.user = payload; + + console.log(`Client connected: ${client.id}, User: ${payload.username}`); + // Willkommensnachricht an den verbundenen Client senden + client.emit('message', { + type: 'system', + text: `Willkommen im Chat, ${payload.username}!`, + timestamp: Date.now(), + }); + } catch (error) { + console.log(`Client ${client.id} rejected: Invalid token`); + client.emit('error', { message: 'Unauthorized: Invalid token' }); + client.disconnect(); + } + } + + private extractTokenFromHandshake(client: Socket): string | null { + // Token aus Cookie extrahieren + const cookies = client.handshake.headers.cookie; + if (cookies) { + const cookieArray = cookies.split(';'); + for (const cookie of cookieArray) { + const [name, value] = cookie.trim().split('='); + if (name === 'access_token') { + return value; + } + } + } + + // Fallback: Token aus auth object + const token = client.handshake.auth?.token; + if (token) { + return token; + } + + // Fallback: Token aus query parameter + const queryToken = client.handshake.query?.token as string; + if (queryToken) { + return queryToken; + } + + return null; + } + + handleDisconnect(client: Socket) { + const user = client.data.user; + if (user) { + console.log(`Client disconnected: ${client.id} (${user.username})`); + + // Notify all rooms the user was in + for (const room of client.rooms) { + if (room.startsWith('group-')) { + const groupId = parseInt(room.replace('group-', '')); + if (!isNaN(groupId)) { + this.server.to(room).emit('message', { + type: 'system', + text: `${user.username} hat den Chat verlassen`, + timestamp: Date.now(), + groupId, + }); + // Also emit specific event if needed + // client.to(room).emit('user-left', { userId: user.sub, groupId }); + } + } + } + } else { + console.log(`Client disconnected: ${client.id}`); + } + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('join-group') + async handleJoinGroup( + @MessageBody() data: { groupId: number }, + @ConnectedSocket() client: Socket, + ) { + const user = client.data.user; + const groupId = data.groupId; + + try { + // Prüfen ob User Mitglied der Gruppe ist + const group = await this.chatService.getGroupById(groupId); + const isMember = group.members.some((m) => m.userId === user.sub); + + if (!isMember) { + client.emit('error', { message: 'Sie sind kein Mitglied dieser Gruppe' }); + return; + } + + // Socket der Gruppe beitreten (für Room-basierte Nachrichten) + client.join(`group-${groupId}`); + console.log(`User ${user.username} joined group ${groupId}`); + + // Bestätigung senden + client.emit('joined-group', { groupId }); + + // System-Nachricht an die Gruppe senden + this.server.to(`group-${groupId}`).emit('message', { + type: 'system', + text: `${user.username} ist dem Chat beigetreten`, + timestamp: Date.now(), + groupId, + }); + } catch (error) { + console.error('Error joining group:', error); + client.emit('error', { message: 'Fehler beim Beitreten zur Gruppe' }); + } + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('leave-group') + handleLeaveGroup( + @MessageBody() data: { groupId: number }, + @ConnectedSocket() client: Socket, + ) { + const user = client.data.user; + const groupId = data.groupId; + + client.leave(`group-${groupId}`); + console.log(`User ${user.username} left group ${groupId}`); + + // System-Nachricht an die Gruppe senden + this.server.to(`group-${groupId}`).emit('message', { + type: 'system', + text: `${user.username} hat den Chat verlassen`, + timestamp: Date.now(), + groupId, + }); + + client.emit('left-group', { groupId }); + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('message') + async handleMessage( + @MessageBody() data: { message: string; groupId: number }, + @ConnectedSocket() client: Socket, + ): Promise { + const user = client.data.user; + console.log(`Message from ${client.id} (${user?.username}) to group ${data.groupId}:`, data.message); + + try { + // Nachricht in DB speichern + const savedMessage = await this.chatService.createMessage( + data.groupId, + user.sub, + data.message, + ); + + // Nachricht an alle Clients in der Gruppe senden + this.server.to(`group-${data.groupId}`).emit('message', { + type: 'chat', + id: savedMessage.id, + message: savedMessage.content, + userId: savedMessage.userId, + username: savedMessage.user.username, + groupId: data.groupId, + timestamp: savedMessage.createdAt.getTime(), + }); + } catch (error) { + console.error('Error saving message:', error); + client.emit('error', { + message: error.message || 'Fehler beim Senden der Nachricht', + }); + } + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('typing') + handleTyping( + @MessageBody() data: { groupId: number; isTyping: boolean }, + @ConnectedSocket() client: Socket, + ): void { + const user = client.data.user; + + // Typing-Indikator an andere Clients in der Gruppe senden (nicht an sich selbst) + client.to(`group-${data.groupId}`).emit('user-typing', { + userId: user.sub, + username: user.username, + groupId: data.groupId, + isTyping: data.isTyping, + }); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/chat/chat.module.ts b/Smartes-Klassenzimmer-Backend/src/chat/chat.module.ts new file mode 100644 index 0000000..977de57 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/chat/chat.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { ChatService } from './chat.service'; +import { ChatGateway } from './chat.gateway'; +import { ChatController } from './chat.controller'; +import { AuthModule } from '../auth/auth.module'; +import { PrismaModule } from '../prisma/prisma.module'; + +@Module({ + imports: [AuthModule, PrismaModule], + controllers: [ChatController], + providers: [ChatGateway, ChatService], +}) +export class ChatModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/chat/chat.service.ts b/Smartes-Klassenzimmer-Backend/src/chat/chat.service.ts new file mode 100644 index 0000000..df9f420 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/chat/chat.service.ts @@ -0,0 +1,332 @@ +import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { CreateGroupDto } from './dto/create-group.dto'; +import { UpdateGroupDto } from './dto/update-group.dto'; +import { AddMembersDto } from './dto/add-members.dto'; + +@Injectable() +export class ChatService { + constructor(private prisma: PrismaService) {} + + // Gruppen-Verwaltung + async createGroup(createdById: number, dto: CreateGroupDto) { + const group = await this.prisma.chatGroup.create({ + data: { + name: dto.name, + description: dto.description, + createdById, + members: { + create: [ + // Ersteller ist automatisch Mitglied + { userId: createdById }, + // Weitere Mitglieder hinzufügen + ...(dto.memberIds || []).map((userId) => ({ userId })), + ], + }, + }, + include: { + members: { + include: { + user: { + select: { + id: true, + username: true, + email: true, + role: true, + }, + }, + }, + }, + }, + }); + + return group; + } + + async getAllGroups() { + return this.prisma.chatGroup.findMany({ + include: { + members: { + include: { + user: { + select: { + id: true, + username: true, + email: true, + role: true, + }, + }, + }, + }, + _count: { + select: { + messages: true, + members: true, + }, + }, + }, + orderBy: { + updatedAt: 'desc', + }, + }); + } + + async getGroupById(groupId: number) { + const group = await this.prisma.chatGroup.findUnique({ + where: { id: groupId }, + include: { + members: { + include: { + user: { + select: { + id: true, + username: true, + email: true, + role: true, + }, + }, + }, + }, + _count: { + select: { + messages: true, + }, + }, + }, + }); + + if (!group) { + throw new NotFoundException('Gruppe nicht gefunden'); + } + + return group; + } + + async getUserGroups(userId: number) { + const memberships = await this.prisma.chatGroupMember.findMany({ + where: { userId }, + include: { + group: { + include: { + members: { + include: { + user: { + select: { + id: true, + username: true, + email: true, + role: true, + }, + }, + }, + }, + _count: { + select: { + messages: true, + members: true, + }, + }, + }, + }, + }, + orderBy: { + group: { + updatedAt: 'desc', + }, + }, + }); + + return memberships.map((m) => m.group); + } + + async updateGroup(groupId: number, userId: number, userRole: string, dto: UpdateGroupDto) { + const group = await this.getGroupById(groupId); + + // Nur Lehrer oder der Ersteller kann die Gruppe bearbeiten + if (userRole !== 'Lehrer' && group.createdById !== userId) { + throw new ForbiddenException('Keine Berechtigung zum Bearbeiten dieser Gruppe'); + } + + return this.prisma.chatGroup.update({ + where: { id: groupId }, + data: { + name: dto.name, + description: dto.description, + }, + include: { + members: { + include: { + user: { + select: { + id: true, + username: true, + email: true, + role: true, + }, + }, + }, + }, + }, + }); + } + + async deleteGroup(groupId: number, userId: number, userRole: string) { + const group = await this.getGroupById(groupId); + + // Nur Lehrer können Gruppen löschen + if (userRole !== 'Lehrer') { + throw new ForbiddenException('Nur Lehrer können Gruppen löschen'); + } + + await this.prisma.chatGroup.delete({ + where: { id: groupId }, + }); + + return { message: 'Gruppe erfolgreich gelöscht' }; + } + + async addMembers(groupId: number, userId: number, userRole: string, dto: AddMembersDto) { + const group = await this.getGroupById(groupId); + + // Nur Lehrer können Mitglieder hinzufügen + if (userRole !== 'Lehrer') { + throw new ForbiddenException('Nur Lehrer können Mitglieder hinzufügen'); + } + + // Mitglieder hinzufügen (ignoriert bereits existierende) + const newMembers = await Promise.all( + dto.userIds.map((userId) => + this.prisma.chatGroupMember.upsert({ + where: { + userId_groupId: { + userId, + groupId, + }, + }, + create: { + userId, + groupId, + }, + update: {}, + include: { + user: { + select: { + id: true, + username: true, + email: true, + role: true, + }, + }, + }, + }), + ), + ); + + return newMembers; + } + + async removeMember(groupId: number, memberUserId: number, requestUserId: number, userRole: string) { + const group = await this.getGroupById(groupId); + + // Nur Lehrer können Mitglieder entfernen + if (userRole !== 'Lehrer') { + throw new ForbiddenException('Nur Lehrer können Mitglieder entfernen'); + } + + // Ersteller kann nicht entfernt werden + if (memberUserId === group.createdById) { + throw new ForbiddenException('Der Ersteller der Gruppe kann nicht entfernt werden'); + } + + await this.prisma.chatGroupMember.delete({ + where: { + userId_groupId: { + userId: memberUserId, + groupId, + }, + }, + }); + + return { message: 'Mitglied erfolgreich entfernt' }; + } + + // Nachrichten-Verwaltung + async getGroupMessages(groupId: number, userId: number, limit = 100, offset = 0) { + // Prüfen ob User Mitglied der Gruppe ist + const membership = await this.prisma.chatGroupMember.findUnique({ + where: { + userId_groupId: { + userId, + groupId, + }, + }, + }); + + if (!membership) { + throw new ForbiddenException('Sie sind kein Mitglied dieser Gruppe'); + } + + const messages = await this.prisma.chatMessage.findMany({ + where: { groupId }, + include: { + user: { + select: { + id: true, + username: true, + email: true, + role: true, + }, + }, + }, + orderBy: { + createdAt: 'desc', + }, + take: limit, + skip: offset, + }); + + return messages.reverse(); // Älteste zuerst + } + + async createMessage(groupId: number, userId: number, content: string) { + // Prüfen ob User Mitglied der Gruppe ist + const membership = await this.prisma.chatGroupMember.findUnique({ + where: { + userId_groupId: { + userId, + groupId, + }, + }, + }); + + if (!membership) { + throw new ForbiddenException('Sie sind kein Mitglied dieser Gruppe'); + } + + const message = await this.prisma.chatMessage.create({ + data: { + content, + userId, + groupId, + }, + include: { + user: { + select: { + id: true, + username: true, + email: true, + role: true, + }, + }, + }, + }); + + // Update group's updatedAt + await this.prisma.chatGroup.update({ + where: { id: groupId }, + data: { updatedAt: new Date() }, + }); + + return message; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/chat/dto/add-members.dto.ts b/Smartes-Klassenzimmer-Backend/src/chat/dto/add-members.dto.ts new file mode 100644 index 0000000..ff455bc --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/chat/dto/add-members.dto.ts @@ -0,0 +1,7 @@ +import { IsArray, IsNumber } from 'class-validator'; + +export class AddMembersDto { + @IsArray() + @IsNumber({}, { each: true }) + userIds: number[]; +} diff --git a/Smartes-Klassenzimmer-Backend/src/chat/dto/create-group.dto.ts b/Smartes-Klassenzimmer-Backend/src/chat/dto/create-group.dto.ts new file mode 100644 index 0000000..fc2ba51 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/chat/dto/create-group.dto.ts @@ -0,0 +1,15 @@ +import { IsString, IsOptional, IsArray, IsNumber } from 'class-validator'; + +export class CreateGroupDto { + @IsString() + name: string; + + @IsOptional() + @IsString() + description?: string; + + @IsOptional() + @IsArray() + @IsNumber({}, { each: true }) + memberIds?: number[]; +} diff --git a/Smartes-Klassenzimmer-Backend/src/chat/dto/update-group.dto.ts b/Smartes-Klassenzimmer-Backend/src/chat/dto/update-group.dto.ts new file mode 100644 index 0000000..5540d3c --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/chat/dto/update-group.dto.ts @@ -0,0 +1,11 @@ +import { IsString, IsOptional } from 'class-validator'; + +export class UpdateGroupDto { + @IsOptional() + @IsString() + name?: string; + + @IsOptional() + @IsString() + description?: string; +} diff --git a/Smartes-Klassenzimmer-Backend/src/classroom/classroom.gateway.ts b/Smartes-Klassenzimmer-Backend/src/classroom/classroom.gateway.ts new file mode 100644 index 0000000..7f31a6b --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/classroom/classroom.gateway.ts @@ -0,0 +1,178 @@ +import { + WebSocketGateway, + WebSocketServer, + SubscribeMessage, + MessageBody, + ConnectedSocket, + OnGatewayConnection, + OnGatewayDisconnect, +} from '@nestjs/websockets'; +import { UseGuards } from '@nestjs/common'; +import { Server, Socket } from 'socket.io'; +import { JwtService } from '@nestjs/jwt'; +import { WsJwtGuard } from '../auth/guards'; +import { ClassroomService } from './classroom.service'; + +@WebSocketGateway({ + namespace: '/api/classroom', + cors: { + origin: [ + 'http://localhost:5173', + 'http://127.0.0.1:5173', + 'http://localhost:5500', + 'http://127.0.0.1:5500', + 'http://localhost:3000' + ], + credentials: true, + }, +}) +export class ClassroomGateway implements OnGatewayConnection, OnGatewayDisconnect { + @WebSocketServer() + server: Server; + + constructor( + private readonly jwtService: JwtService, + private readonly classroomService: ClassroomService, + ) {} + + handleConnection(client: Socket) { + try { + const token = this.extractTokenFromHandshake(client); + + if (!token) { + console.log(`Classroom: Client ${client.id} rejected: No token`); + client.disconnect(); + return; + } + + const payload = this.jwtService.verify(token, { + secret: process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production', + }); + + client.data.user = payload; + + // Send current state to newly connected user + client.emit('state-update', { + raisedHands: this.classroomService.getRaisedHands(), + activeStudent: this.classroomService.getActiveStudent(), + }); + + } catch (error) { + console.log(`Classroom: Client ${client.id} rejected: Invalid token`); + client.disconnect(); + } + } + + handleDisconnect(client: Socket) { + // Optional: automatically lower hand on disconnect? + // For now, let's keep it persistent for 10 mins as per requirements. + } + + private extractTokenFromHandshake(client: Socket): string | null { + const cookies = client.handshake.headers.cookie; + if (cookies) { + const cookieArray = cookies.split(';'); + for (const cookie of cookieArray) { + const [name, value] = cookie.trim().split('='); + if (name === 'access_token') { + return value; + } + } + } + return client.handshake.auth?.token || (client.handshake.query?.token as string) || null; + } + + private broadcastState() { + this.server.emit('state-update', { + raisedHands: this.classroomService.getRaisedHands(), + activeStudent: this.classroomService.getActiveStudent(), + }); + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('raise-hand') + handleRaiseHand( + @MessageBody() data: { type: 'normal' | 'question' }, + @ConnectedSocket() client: Socket, + ) { + const user = client.data.user; + this.classroomService.raiseHand(user.sub, user.username, data.type, (userId) => { + // Callback for timeout + this.classroomService.lowerHand(userId); + this.broadcastState(); + }); + this.broadcastState(); + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('lower-hand') + handleLowerHand(@ConnectedSocket() client: Socket) { + const user = client.data.user; + this.classroomService.lowerHand(user.sub); + this.broadcastState(); + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('lower-all-hands') + handleLowerAllHands(@ConnectedSocket() client: Socket) { + const user = client.data.user; + if (user.role !== 'Lehrer') { + client.emit('error', { message: 'Nur Lehrer können alle Hände senken.' }); + return; + } + this.classroomService.lowerAllHands(); + this.broadcastState(); + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('pick-student') + handlePickStudent( + @MessageBody() data: { userId: number }, + @ConnectedSocket() client: Socket, + ) { + const user = client.data.user; + if (user.role !== 'Lehrer') { + client.emit('error', { message: 'Nur Lehrer können Schüler aufrufen.' }); + return; + } + + const hands = this.classroomService.getRaisedHands(); + const target = hands.find(h => h.userId === data.userId); + + if (target) { + this.classroomService.setActiveStudent(target.userId, target.username); + this.broadcastState(); + } + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('pick-random') + handlePickRandom(@ConnectedSocket() client: Socket) { + const user = client.data.user; + if (user.role !== 'Lehrer') { + client.emit('error', { message: 'Nur Lehrer können Zufallsauswahl nutzen.' }); + return; + } + + this.classroomService.pickRandomStudent(); + this.broadcastState(); + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('reset-active') + handleResetActive(@ConnectedSocket() client: Socket) { + const user = client.data.user; + const activeStudent = this.classroomService.getActiveStudent(); + + // Allow if teacher OR if the requesting user is the active student + const isActiveStudent = activeStudent && activeStudent.userId === user.sub; + + if (user.role !== 'Lehrer' && !isActiveStudent) { + client.emit('error', { message: 'Nur Lehrer oder der aktive Schüler können die Auswahl zurücksetzen.' }); + return; + } + + this.classroomService.clearActiveStudent(); + this.broadcastState(); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/classroom/classroom.module.ts b/Smartes-Klassenzimmer-Backend/src/classroom/classroom.module.ts new file mode 100644 index 0000000..3790034 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/classroom/classroom.module.ts @@ -0,0 +1,18 @@ +import { Module } from '@nestjs/common'; +import { ClassroomGateway } from './classroom.gateway'; +import { ClassroomService } from './classroom.service'; +import { AuthModule } from '../auth/auth.module'; +import { JwtModule } from '@nestjs/jwt'; + +@Module({ + imports: [ + AuthModule, + JwtModule.register({ + secret: process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production', + signOptions: { expiresIn: '7d' }, + }), + ], + providers: [ClassroomGateway, ClassroomService], + exports: [ClassroomService], +}) +export class ClassroomModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/classroom/classroom.service.ts b/Smartes-Klassenzimmer-Backend/src/classroom/classroom.service.ts new file mode 100644 index 0000000..437ceef --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/classroom/classroom.service.ts @@ -0,0 +1,80 @@ +import { Injectable } from '@nestjs/common'; + +export interface RaisedHand { + userId: number; + username: string; + type: 'normal' | 'question'; + raisedAt: Date; + timeoutId?: NodeJS.Timeout; +} + +@Injectable() +export class ClassroomService { + private raisedHands: Map = new Map(); + private activeStudent: { userId: number; username: string } | null = null; + + raiseHand(userId: number, username: string, type: 'normal' | 'question', onTimeout: (userId: number) => void) { + // Clear existing timeout if re-raising or changing type + const existingHand = this.raisedHands.get(userId); + if (existingHand) { + clearTimeout(existingHand.timeoutId); + } + + const timeoutId = setTimeout(() => { + onTimeout(userId); + }, 10 * 60 * 1000); // 10 minutes + + this.raisedHands.set(userId, { + userId, + username, + type, + raisedAt: new Date(), + timeoutId, + }); + } + + lowerHand(userId: number) { + const hand = this.raisedHands.get(userId); + if (hand) { + clearTimeout(hand.timeoutId); + this.raisedHands.delete(userId); + return true; + } + return false; + } + + lowerAllHands() { + this.raisedHands.forEach((hand) => clearTimeout(hand.timeoutId)); + this.raisedHands.clear(); + } + + getRaisedHands() { + // Return array without timeoutId (not serializable/needed for frontend) + return Array.from(this.raisedHands.values()).map(({ timeoutId, ...rest }) => rest); + } + + setActiveStudent(userId: number, username: string) { + this.activeStudent = { userId, username }; + // Also lower the hand of the picked student + this.lowerHand(userId); + } + + clearActiveStudent() { + this.activeStudent = null; + } + + getActiveStudent() { + return this.activeStudent; + } + + pickRandomStudent(): { userId: number; username: string } | null { + const hands = Array.from(this.raisedHands.values()); + if (hands.length === 0) return null; + + const randomIndex = Math.floor(Math.random() * hands.length); + const selected = hands[randomIndex]; + + this.setActiveStudent(selected.userId, selected.username); + return { userId: selected.userId, username: selected.username }; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/feedback/dto/create-feedback.dto.ts b/Smartes-Klassenzimmer-Backend/src/feedback/dto/create-feedback.dto.ts new file mode 100644 index 0000000..d4b9bb0 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/feedback/dto/create-feedback.dto.ts @@ -0,0 +1,96 @@ +import { + IsNumber, + IsBoolean, + IsOptional, + IsDateString, + Min, + Max, + ValidateNested, + IsInt, + IsString, + MaxLength +} from 'class-validator'; +import { Type } from 'class-transformer'; + +class FeedbackCategories { + @IsOptional() + @IsNumber() + @Min(1, { message: 'Clarity rating must be between 1 and 10' }) + @Max(10, { message: 'Clarity rating must be between 1 and 10' }) + clarity?: number; + + @IsOptional() + @IsNumber() + @Min(1, { message: 'Pace rating must be between 1 and 10' }) + @Max(10, { message: 'Pace rating must be between 1 and 10' }) + pace?: number; + + @IsOptional() + @IsNumber() + @Min(1, { message: 'Interaction rating must be between 1 and 10' }) + @Max(10, { message: 'Interaction rating must be between 1 and 10' }) + interaction?: number; + + @IsOptional() + @IsNumber() + @Min(1, { message: 'Materials rating must be between 1 and 10' }) + @Max(10, { message: 'Materials rating must be between 1 and 10' }) + materials?: number; + + @IsOptional() + @IsNumber() + @Min(1, { message: 'Atmosphere rating must be between 1 and 10' }) + @Max(10, { message: 'Atmosphere rating must be between 1 and 10' }) + atmosphere?: number; +} + +export class CreateFeedbackDto { + @Type(() => Number) + @IsInt({ message: 'Student ID must be an integer' }) + studentId: number; + + @Type(() => Number) + @IsInt({ message: 'Teacher ID must be an integer' }) + teacherId: number; + + @Type(() => Number) + @IsInt({ message: 'Lesson ID must be an integer' }) + lessonId: number; + + @IsDateString({}, { message: 'Lesson date must be a valid ISO 8601 date string' }) + lessonDate: Date; + + @Type(() => Number) + @IsNumber() + @Min(1, { message: 'Overall rating must be between 1 and 10' }) + @Max(10, { message: 'Overall rating must be between 1 and 10' }) + overallRating: number; + + @IsOptional() + @ValidateNested() + @Type(() => FeedbackCategories) + categories?: FeedbackCategories; + + @IsOptional() + @IsString() + @MaxLength(2000, { message: 'What was good text cannot exceed 2000 characters' }) + whatWasGood?: string; + + @IsOptional() + @IsString() + @MaxLength(2000, { message: 'What can improve text cannot exceed 2000 characters' }) + whatCanImprove?: string; + + @IsOptional() + @IsString() + @MaxLength(2000, { message: 'Additional comments cannot exceed 2000 characters' }) + additionalComments?: string; + + @IsOptional() + @IsBoolean() + isAnonymous?: boolean; + + @IsOptional() + @IsBoolean() + allowTeacherResponse?: boolean; +} diff --git a/Smartes-Klassenzimmer-Backend/src/feedback/dto/update-feedback.dto.ts b/Smartes-Klassenzimmer-Backend/src/feedback/dto/update-feedback.dto.ts new file mode 100644 index 0000000..4383870 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/feedback/dto/update-feedback.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateFeedbackDto } from './create-feedback.dto'; + +export class UpdateFeedbackDto extends PartialType(CreateFeedbackDto) {} diff --git a/Smartes-Klassenzimmer-Backend/src/feedback/feedback.controller.ts b/Smartes-Klassenzimmer-Backend/src/feedback/feedback.controller.ts new file mode 100644 index 0000000..ccc8323 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/feedback/feedback.controller.ts @@ -0,0 +1,133 @@ +import { Controller, Get, Post, Body, Patch, Param, Delete, Query, UseGuards, Req, BadRequestException } from '@nestjs/common'; +import type { Request } from 'express'; +import { FeedbackService } from './feedback.service'; +import { CreateFeedbackDto } from './dto/create-feedback.dto'; +import { UpdateFeedbackDto } from './dto/update-feedback.dto'; +import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; +import { RolesGuard, Roles } from '../auth/guards/roles.guard'; + +@Controller('feedback') +export class FeedbackController { + constructor(private readonly feedbackService: FeedbackService) {} + + // Feedback einreichen (nur von Schülern) + @Post() + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(['Student']) + create(@Body() createFeedbackDto: CreateFeedbackDto) { + return this.feedbackService.create(createFeedbackDto); + } + @Get() + getChatInfo() :object { + return{ + message: 'Feedback API is running' + } + } + + // Alle Feedbacks abrufen - NUR FÜR DEN EINGELOGGTEN LEHRER + @Get('all') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(['Lehrer']) + findAll(@Req() req: Request) { + const user = req.user as any; + const teacherId = Number(user.id); + if (isNaN(teacherId)) { + throw new BadRequestException('Ungültige Lehrer-ID'); + } + return this.feedbackService.findByTeacher(teacherId); + } + + // Einzelnes Feedback abrufen - NUR FÜR LEHRER + @Get(':id') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(['Lehrer']) + findOne(@Param('id') id: string) { + return this.feedbackService.findOne(id); + } + + // Feedbacks nach Lehrer-ID - NUR FÜR LEHRER + @Get('teacher/:teacherId') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(['Lehrer']) + findByTeacher(@Param('teacherId') teacherId: string) { + const id = parseInt(teacherId); + if (isNaN(id)) { + throw new BadRequestException('Ungültige Lehrer-ID'); + } + return this.feedbackService.findByTeacher(id); + } + + // Feedbacks nach Schüler-ID - Authentifizierung erforderlich + @Get('student/:studentId') + @UseGuards(JwtAuthGuard) + findByStudent(@Param('studentId') studentId: string) { + const id = parseInt(studentId); + if (isNaN(id)) { + throw new BadRequestException('Ungültige Schüler-ID'); + } + return this.feedbackService.findByStudent(id); + } + + // Feedbacks nach Unterrichtsstunden-ID - Authentifizierung erforderlich + @Get('lesson/:lessonId') + @UseGuards(JwtAuthGuard) + findByLesson(@Param('lessonId') lessonId: string) { + const id = parseInt(lessonId); + if (isNaN(id)) { + throw new BadRequestException('Ungültige Unterrichtsstunden-ID'); + } + return this.feedbackService.findByLesson(id); + } + + // Statistiken für einen Lehrer - NUR FÜR LEHRER + @Get('statistics/teacher/:teacherId') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(['Lehrer']) + getTeacherStatistics(@Param('teacherId') teacherId: string) { + const id = parseInt(teacherId); + if (isNaN(id)) { + throw new BadRequestException('Ungültige Lehrer-ID'); + } + return this.feedbackService.getTeacherStatistics(id); + } + + // Statistiken für eine Unterrichtsstunde - NUR FÜR LEHRER + @Get('statistics/lesson/:lessonId') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(['Lehrer']) + getLessonStatistics(@Param('lessonId') lessonId: string) { + const id = parseInt(lessonId); + if (isNaN(id)) { + throw new BadRequestException('Ungültige Unterrichtsstunden-ID'); + } + return this.feedbackService.getLessonStatistics(id); + } + + // Lehrer antwortet auf Feedback - NUR FÜR LEHRER + @Post(':id/response') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(['Lehrer']) + addTeacherResponse( + @Param('id') id: string, + @Body() body: { response: string }, + ) { + return this.feedbackService.addTeacherResponse(id, body.response); + } + + // Feedback aktualisieren - NUR FÜR LEHRER + @Patch(':id') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(['Lehrer']) + update(@Param('id') id: string, @Body() updateFeedbackDto: UpdateFeedbackDto) { + return this.feedbackService.update(id, updateFeedbackDto); + } + + // Feedback löschen - NUR FÜR LEHRER + @Delete(':id') + @UseGuards(JwtAuthGuard, RolesGuard) + @Roles(['Lehrer']) + async remove(@Param('id') id: string) { + await this.feedbackService.remove(id); + return { success: true, message: 'Feedback erfolgreich gelöscht' }; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/feedback/feedback.module.ts b/Smartes-Klassenzimmer-Backend/src/feedback/feedback.module.ts new file mode 100644 index 0000000..643d07d --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/feedback/feedback.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { FeedbackService } from './feedback.service'; +import { FeedbackController } from './feedback.controller'; +import { PrismaModule } from '../prisma/prisma.module'; + +@Module({ + imports: [PrismaModule], + controllers: [FeedbackController], + providers: [FeedbackService], +}) +export class FeedbackModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/feedback/feedback.service.ts b/Smartes-Klassenzimmer-Backend/src/feedback/feedback.service.ts new file mode 100644 index 0000000..db4d144 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/feedback/feedback.service.ts @@ -0,0 +1,623 @@ +import { Injectable, NotFoundException, BadRequestException, ConflictException } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { CreateFeedbackDto } from './dto/create-feedback.dto'; +import { UpdateFeedbackDto } from './dto/update-feedback.dto'; +import { Prisma } from '@prisma/client'; +import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'; + +// Custom Feedback Interface für API Response (mit Relationen) +export interface Feedback { + id: string; + studentId: number; + student?: { + id: number; + username: string; + email: string; + }; + teacherId: number; + teacher?: { + id: number; + username: string; + email: string; + }; + lessonId: number; + timetableEntry?: { + id: number; + subject: { + id: number; + name: string; + abbreviation?: string | null; + color?: string | null; + }; + teacher?: { + id: number; + username: string; + email: string; + } | null; + room?: { + id: number; + number: string; + } | null; + dayOfWeek: number; + startTime: string; + endTime: string; + }; + lessonDate: Date; + overallRating: number; + categories?: any; + whatWasGood?: string | null; + whatCanImprove?: string | null; + additionalComments?: string | null; + isAnonymous: boolean; + allowTeacherResponse: boolean; + teacherResponse?: string | null; + teacherRespondedAt?: Date | null; + createdAt?: Date; + updatedAt?: Date; +} + +@Injectable() +export class FeedbackService { + constructor(private prisma: PrismaService) {} + + /** + * Hilfsfunktion: Entfernt student-Details bei anonymen Feedbacks + */ + private sanitizeFeedback(feedback: any): Feedback { + if (feedback.isAnonymous) { + const { student, studentId, ...sanitizedFeedback } = feedback; + return this.convertToFeedback(sanitizedFeedback); + } + return this.convertToFeedback(feedback); + } + + /** + * Hilfsfunktion: Sanitiert ein Array von Feedbacks + */ + private sanitizeFeedbacks(feedbacks: any[]): Feedback[] { + return feedbacks.map(fb => this.sanitizeFeedback(fb)); + } + + /** + * Konvertiert Prisma Feedback zu API Feedback + */ + private convertToFeedback(prismaFeedback: any): Feedback { + return { + id: prismaFeedback.id?.toString(), + studentId: prismaFeedback.studentId, + student: prismaFeedback.student, + teacherId: prismaFeedback.teacherId, + teacher: prismaFeedback.teacher, + lessonId: prismaFeedback.lessonId, + timetableEntry: prismaFeedback.timetableEntry, + lessonDate: prismaFeedback.lessonDate, + overallRating: prismaFeedback.overallRating, + categories: prismaFeedback.categories as any, + whatWasGood: prismaFeedback.whatWasGood, + whatCanImprove: prismaFeedback.whatCanImprove, + additionalComments: prismaFeedback.additionalComments, + isAnonymous: prismaFeedback.isAnonymous, + allowTeacherResponse: prismaFeedback.allowTeacherResponse, + teacherResponse: prismaFeedback.teacherResponse, + teacherRespondedAt: prismaFeedback.teacherRespondedAt, + createdAt: prismaFeedback.createdAt, + updatedAt: prismaFeedback.updatedAt, + }; + } + + async create(createFeedbackDto: CreateFeedbackDto): Promise { + try { + // Validiere Foreign Key Beziehungen + const [student, teacher, lesson] = await Promise.all([ + this.prisma.user.findUnique({ where: { id: createFeedbackDto.studentId } }), + this.prisma.user.findUnique({ where: { id: createFeedbackDto.teacherId } }), + this.prisma.timetableEntry.findUnique({ where: { id: createFeedbackDto.lessonId } }), + ]); + + if (!student) { + throw new NotFoundException(`Schüler mit ID ${createFeedbackDto.studentId} nicht gefunden`); + } + + if (!teacher) { + throw new NotFoundException(`Lehrer mit ID ${createFeedbackDto.teacherId} nicht gefunden`); + } + + if (!lesson) { + throw new NotFoundException(`Unterrichtsstunde mit ID ${createFeedbackDto.lessonId} nicht gefunden`); + } + + // Erstelle Feedback mit Transaction für Datenintegrität + const feedback = await this.prisma.$transaction(async (tx) => { + return await tx.feedback.create({ + data: { + studentId: createFeedbackDto.studentId, + teacherId: createFeedbackDto.teacherId, + lessonId: createFeedbackDto.lessonId, + lessonDate: new Date(createFeedbackDto.lessonDate), + overallRating: createFeedbackDto.overallRating, + categories: createFeedbackDto.categories ? JSON.parse(JSON.stringify(createFeedbackDto.categories)) : null, + whatWasGood: createFeedbackDto.whatWasGood, + whatCanImprove: createFeedbackDto.whatCanImprove, + additionalComments: createFeedbackDto.additionalComments, + isAnonymous: createFeedbackDto.isAnonymous || false, + allowTeacherResponse: createFeedbackDto.allowTeacherResponse !== undefined + ? createFeedbackDto.allowTeacherResponse + : true, + }, + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: { + select: { + id: true, + number: true, + }, + }, + }, + }, + }, + }); + }); + + return this.sanitizeFeedback(feedback); + } catch (error) { + // Handle Prisma Unique Constraint Error + if (error instanceof PrismaClientKnownRequestError) { + if (error.code === 'P2002') { + throw new ConflictException('Sie haben für diese Stunde bereits Feedback abgegeben'); + } + if (error.code === 'P2003') { + throw new BadRequestException('Ungültige Referenz: Schüler, Lehrer oder Unterrichtsstunde existiert nicht'); + } + } + throw error; + } + } + + async findAll(): Promise { + const feedbacks = await this.prisma.feedback.findMany({ + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: { + select: { + id: true, + number: true, + }, + }, + }, + }, + }, + orderBy: { createdAt: 'desc' }, + }); + return this.sanitizeFeedbacks(feedbacks); + } + + async findOne(id: string): Promise { + const feedback = await this.prisma.feedback.findUnique({ + where: { id: parseInt(id) }, + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: { + select: { + id: true, + number: true, + }, + }, + }, + }, + }, + }); + + if (!feedback) { + throw new NotFoundException(`Feedback mit ID ${id} nicht gefunden`); + } + + return this.sanitizeFeedback(feedback); + } + + async findByTeacher(teacherId: number): Promise { + const feedbacks = await this.prisma.feedback.findMany({ + where: { teacherId }, + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: { + select: { + id: true, + number: true, + }, + }, + }, + }, + }, + orderBy: { createdAt: 'desc' }, + }); + return this.sanitizeFeedbacks(feedbacks); + } + + async findByStudent(studentId: number): Promise { + const feedbacks = await this.prisma.feedback.findMany({ + where: { studentId }, + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: { + select: { + id: true, + number: true, + }, + }, + }, + }, + }, + orderBy: { createdAt: 'desc' }, + }); + return this.sanitizeFeedbacks(feedbacks); + } + + async findByLesson(lessonId: number): Promise { + const feedbacks = await this.prisma.feedback.findMany({ + where: { lessonId }, + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: { + select: { + id: true, + number: true, + }, + }, + }, + }, + }, + orderBy: { createdAt: 'desc' }, + }); + return this.sanitizeFeedbacks(feedbacks); + } + + async update(id: string, updateFeedbackDto: UpdateFeedbackDto): Promise { + const feedbackId = parseInt(id); + if (isNaN(feedbackId)) { + throw new BadRequestException('Ungültige Feedback-ID'); + } + + try { + const updatedFeedback = await this.prisma.$transaction(async (tx) => { + const feedback = await tx.feedback.findUnique({ + where: { id: feedbackId }, + }); + + if (!feedback) { + throw new NotFoundException(`Feedback mit ID ${id} nicht gefunden`); + } + + return await tx.feedback.update({ + where: { id: feedbackId }, + data: { + ...updateFeedbackDto, + categories: updateFeedbackDto.categories + ? JSON.parse(JSON.stringify(updateFeedbackDto.categories)) + : undefined, + }, + }); + }); + + return this.sanitizeFeedback(updatedFeedback); + } catch (error) { + if (error instanceof NotFoundException) { + throw error; + } + throw new BadRequestException('Fehler beim Aktualisieren des Feedbacks'); + } + } + + async remove(id: string): Promise { + const feedbackId = parseInt(id); + if (isNaN(feedbackId)) { + throw new BadRequestException('Ungültige Feedback-ID'); + } + + try { + await this.prisma.$transaction(async (tx) => { + const feedback = await tx.feedback.findUnique({ + where: { id: feedbackId }, + }); + + if (!feedback) { + throw new NotFoundException(`Feedback mit ID ${id} nicht gefunden`); + } + + await tx.feedback.delete({ + where: { id: feedbackId }, + }); + }); + } catch (error) { + if (error instanceof NotFoundException) { + throw error; + } + throw new BadRequestException('Fehler beim Löschen des Feedbacks'); + } + } + + async addTeacherResponse(id: string, response: string): Promise { + const feedbackId = parseInt(id); + if (isNaN(feedbackId)) { + throw new BadRequestException('Ungültige Feedback-ID'); + } + + if (!response || response.trim().length === 0) { + throw new BadRequestException('Die Antwort darf nicht leer sein'); + } + + try { + const updatedFeedback = await this.prisma.$transaction(async (tx) => { + const feedback = await tx.feedback.findUnique({ + where: { id: feedbackId }, + }); + + if (!feedback) { + throw new NotFoundException(`Feedback mit ID ${id} nicht gefunden`); + } + + if (!feedback.allowTeacherResponse) { + throw new BadRequestException('Lehrer-Antwort ist für dieses Feedback nicht erlaubt'); + } + + return await tx.feedback.update({ + where: { id: feedbackId }, + data: { + teacherResponse: response, + teacherRespondedAt: new Date(), + }, + }); + }); + + return this.sanitizeFeedback(updatedFeedback); + } catch (error) { + if (error instanceof NotFoundException || error instanceof BadRequestException) { + throw error; + } + throw new BadRequestException('Fehler beim Hinzufügen der Lehrer-Antwort'); + } + } + + // Statistiken für Lehrer + async getTeacherStatistics(teacherId: number) { + const teacherFeedbacks = await this.findByTeacher(teacherId); + + if (teacherFeedbacks.length === 0) { + return { + totalFeedbacks: 0, + averageRating: 0, + categoryAverages: { + clarity: 0, + pace: 0, + interaction: 0, + materials: 0, + atmosphere: 0, + }, + ratingDistribution: { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0 }, + }; + } + + // Durchschnittliche Gesamtbewertung + const averageRating = + teacherFeedbacks.reduce((sum, fb) => sum + fb.overallRating, 0) / + teacherFeedbacks.length; + + // Kategorien-Durchschnitte + const categories: { + clarity: number[]; + pace: number[]; + interaction: number[]; + materials: number[]; + atmosphere: number[]; + } = { + clarity: [], + pace: [], + interaction: [], + materials: [], + atmosphere: [], + }; + + teacherFeedbacks.forEach((fb) => { + if (fb.categories) { + if (fb.categories.clarity) categories.clarity.push(fb.categories.clarity); + if (fb.categories.pace) categories.pace.push(fb.categories.pace); + if (fb.categories.interaction) categories.interaction.push(fb.categories.interaction); + if (fb.categories.materials) categories.materials.push(fb.categories.materials); + if (fb.categories.atmosphere) categories.atmosphere.push(fb.categories.atmosphere); + } + }); + + const categoryAverages = { + clarity: categories.clarity.length > 0 + ? categories.clarity.reduce((a, b) => a + b, 0) / categories.clarity.length + : 0, + pace: categories.pace.length > 0 + ? categories.pace.reduce((a, b) => a + b, 0) / categories.pace.length + : 0, + interaction: categories.interaction.length > 0 + ? categories.interaction.reduce((a, b) => a + b, 0) / categories.interaction.length + : 0, + materials: categories.materials.length > 0 + ? categories.materials.reduce((a, b) => a + b, 0) / categories.materials.length + : 0, + atmosphere: categories.atmosphere.length > 0 + ? categories.atmosphere.reduce((a, b) => a + b, 0) / categories.atmosphere.length + : 0, + }; + + // Verteilung der Bewertungen (1-10) + const ratingDistribution = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0 }; + teacherFeedbacks.forEach((fb) => { + if (fb.overallRating >= 1 && fb.overallRating <= 10) { + ratingDistribution[fb.overallRating]++; + } + }); + + return { + totalFeedbacks: teacherFeedbacks.length, + averageRating: Math.round(averageRating * 10) / 10, + categoryAverages, + ratingDistribution, + }; + } + + // Statistiken für eine Unterrichtsstunde + async getLessonStatistics(lessonId: number) { + const lessonFeedbacks = await this.findByLesson(lessonId); + + if (lessonFeedbacks.length === 0) { + return { + totalFeedbacks: 0, + averageRating: 0, + feedbacks: [], + }; + } + + const averageRating = + lessonFeedbacks.reduce((sum, fb) => sum + fb.overallRating, 0) / + lessonFeedbacks.length; + + return { + totalFeedbacks: lessonFeedbacks.length, + averageRating: Math.round(averageRating * 10) / 10, + feedbacks: lessonFeedbacks, // bereits sanitiert durch findByLesson + }; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/files/files.controller.ts b/Smartes-Klassenzimmer-Backend/src/files/files.controller.ts new file mode 100644 index 0000000..9853f35 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/files/files.controller.ts @@ -0,0 +1,87 @@ +import { Controller, Get, Post, Param, Delete, UseGuards, UploadedFile, UseInterceptors, Body, ForbiddenException, NotFoundException, Req, Res, ParseIntPipe } from '@nestjs/common'; +import { FilesService } from './files.service'; +import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { diskStorage } from 'multer'; +import { extname } from 'path'; +import type { Response } from 'express'; +import { PrismaService } from '../prisma/prisma.service'; +import * as fs from 'fs'; + +@Controller('files') +@UseGuards(JwtAuthGuard) +export class FilesController { + constructor( + private readonly filesService: FilesService, + private readonly prisma: PrismaService + ) {} + + @Post('upload') + @UseInterceptors(FileInterceptor('file', { + storage: diskStorage({ + destination: './uploads', + filename: (req, file, callback) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + const ext = extname(file.originalname); + callback(null, `${uniqueSuffix}${ext}`); + }, + }), + })) + async uploadFile(@UploadedFile() file: Express.Multer.File, @Body('timetableEntryId') timetableEntryId: string, @Req() req: any) { + const user = req.user; + const entryId = timetableEntryId ? parseInt(timetableEntryId) : undefined; + + if (!file) { + throw new NotFoundException('No file uploaded'); + } + + try { + if (entryId) { + // Lesson Upload + const lesson = await this.prisma.timetableEntry.findUnique({ where: { id: entryId } }); + if (!lesson) throw new NotFoundException('Lesson not found'); + + if (user.role !== 'Lehrer') { + // Student uploading to lesson + if (!lesson.allowStudentUploads) { + throw new ForbiddenException('Uploads not allowed for this lesson'); + } + } + } else { + // General Upload + if (user.role !== 'Lehrer') { + throw new ForbiddenException('Only teachers can upload general files'); + } + } + + return await this.filesService.saveFile(file, user.id, entryId); + } catch (error) { + // Cleanup file if error + if (file.path && fs.existsSync(file.path)) { + fs.unlinkSync(file.path); + } + throw error; + } + } + + @Get() + async findAllGeneral() { + return this.filesService.findAllGeneral(); + } + + @Get('lesson/:id') + async findByLesson(@Param('id', ParseIntPipe) id: number, @Req() req: any) { + return this.filesService.findByLesson(id, req.user.id, req.user.role); + } + + @Get('download/:id') + async downloadFile(@Param('id', ParseIntPipe) id: number, @Res() res: Response) { + const fileEntity = await this.filesService.getFileEntity(id); + res.download(fileEntity.path, fileEntity.filename); + } + + @Delete(':id') + async deleteFile(@Param('id', ParseIntPipe) id: number, @Req() req: any) { + return this.filesService.deleteFile(id, req.user.id, req.user.role); + } +} \ No newline at end of file diff --git a/Smartes-Klassenzimmer-Backend/src/files/files.module.ts b/Smartes-Klassenzimmer-Backend/src/files/files.module.ts new file mode 100644 index 0000000..dbe3cf1 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/files/files.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { FilesController } from './files.controller'; +import { FilesService } from './files.service'; +import { PrismaModule } from '../prisma/prisma.module'; + +@Module({ + imports: [PrismaModule], + controllers: [FilesController], + providers: [FilesService] +}) +export class FilesModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/files/files.service.ts b/Smartes-Klassenzimmer-Backend/src/files/files.service.ts new file mode 100644 index 0000000..21cd891 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/files/files.service.ts @@ -0,0 +1,93 @@ +import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import * as fs from 'fs'; +import * as path from 'path'; + +@Injectable() +export class FilesService { + private uploadDir = 'uploads'; + + constructor(private prisma: PrismaService) { + // Ensure upload directory exists + if (!fs.existsSync(this.uploadDir)) { + fs.mkdirSync(this.uploadDir, { recursive: true }); + } + } + + async saveFile(file: Express.Multer.File, uploaderId: number, timetableEntryId?: number) { + return this.prisma.file.create({ + data: { + filename: file.originalname, + path: file.path, + mimetype: file.mimetype, + size: file.size, + uploadedById: uploaderId, + timetableEntryId: timetableEntryId ? Number(timetableEntryId) : null, + }, + include: { + uploadedBy: { + select: { + username: true, + role: true + } + } + } + }); + } + + async findAllGeneral() { + return this.prisma.file.findMany({ + where: { timetableEntryId: null }, + include: { uploadedBy: { select: { username: true, role: true } } }, + orderBy: { createdAt: 'desc' } + }); + } + + async findByLesson(lessonId: number, userId: number, userRole: string) { + const lessonIdNum = Number(lessonId); + if (userRole === 'Lehrer') { + return this.prisma.file.findMany({ + where: { timetableEntryId: lessonIdNum }, + include: { uploadedBy: { select: { id: true, username: true, role: true } } }, + orderBy: { createdAt: 'desc' } + }); + } else { + return this.prisma.file.findMany({ + where: { + timetableEntryId: lessonIdNum, + OR: [ + { uploadedBy: { role: 'Lehrer' } }, + { uploadedById: userId } + ] + }, + include: { uploadedBy: { select: { id: true, username: true, role: true } } }, + orderBy: { createdAt: 'desc' } + }); + } + } + + async getFileEntity(id: number) { + const file = await this.prisma.file.findUnique({ where: { id } }); + if (!file) throw new NotFoundException('File not found'); + return file; + } + + async deleteFile(id: number, userId: number, userRole: string) { + const file = await this.prisma.file.findUnique({ where: { id } }); + if (!file) throw new NotFoundException('File not found'); + + if (userRole !== 'Lehrer' && file.uploadedById !== userId) { + throw new ForbiddenException('You can only delete your own files'); + } + + await this.prisma.file.delete({ where: { id } }); + try { + if (fs.existsSync(file.path)) { + fs.unlinkSync(file.path); + } + } catch (e) { + console.error("Failed to delete file from disk", e); + } + return { success: true }; + } +} \ No newline at end of file diff --git a/Smartes-Klassenzimmer-Backend/src/grades/dto/create-grade.dto.ts b/Smartes-Klassenzimmer-Backend/src/grades/dto/create-grade.dto.ts new file mode 100644 index 0000000..8f012b0 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/grades/dto/create-grade.dto.ts @@ -0,0 +1,51 @@ +import { IsInt, IsNumber, IsString, IsOptional, IsDateString, Min, Max } from 'class-validator'; + +export class CreateGradeDto { + @IsInt() + studentId: number; + + @IsInt() + timetableEntryId: number; + + @IsInt() + @Min(1) + @Max(53) + weekNumber: number; + + @IsInt() + year: number; + + @IsNumber() + @Min(1.0) + @Max(6.0) + grade: number; + + @IsString() + gradeType: string; // z.B. "Klausur", "Mitarbeit", "Hausaufgabe", "Mündlich" + + @IsOptional() + @IsNumber() + weight?: number; + + @IsOptional() + @IsString() + title?: string; + + @IsOptional() + @IsString() + description?: string; + + @IsOptional() + @IsNumber() + maxPoints?: number; + + @IsOptional() + @IsNumber() + achievedPoints?: number; + + @IsDateString() + date: string; + + @IsInt() + teacherId: number; // Foreign Key zu User (Lehrer) +} diff --git a/Smartes-Klassenzimmer-Backend/src/grades/dto/index.ts b/Smartes-Klassenzimmer-Backend/src/grades/dto/index.ts new file mode 100644 index 0000000..6debbae --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/grades/dto/index.ts @@ -0,0 +1,2 @@ +export * from './create-grade.dto'; +export * from './update-grade.dto'; diff --git a/Smartes-Klassenzimmer-Backend/src/grades/dto/update-grade.dto.ts b/Smartes-Klassenzimmer-Backend/src/grades/dto/update-grade.dto.ts new file mode 100644 index 0000000..9041ad2 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/grades/dto/update-grade.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateGradeDto } from './create-grade.dto'; + +export class UpdateGradeDto extends PartialType(CreateGradeDto) {} diff --git a/Smartes-Klassenzimmer-Backend/src/grades/grades.controller.ts b/Smartes-Klassenzimmer-Backend/src/grades/grades.controller.ts new file mode 100644 index 0000000..81bf7d3 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/grades/grades.controller.ts @@ -0,0 +1,147 @@ +import { + Controller, + Get, + Post, + Put, + Delete, + Body, + Param, + Query, + UseGuards, + Request, + ParseIntPipe, + ForbiddenException, +} from '@nestjs/common'; +import { GradesService } from './grades.service'; +import { CreateGradeDto, UpdateGradeDto } from './dto'; +import { JwtAuthGuard, RolesGuard } from '../auth/guards'; +import { Roles } from '../auth/guards/roles.guard'; + +@Controller('grades') +@UseGuards(JwtAuthGuard, RolesGuard) +export class GradesController { + constructor(private readonly gradesService: GradesService) {} + + // Lehrer: Note erstellen + @Post() + @Roles(['Lehrer']) + async createGrade(@Body() createGradeDto: CreateGradeDto) { + return this.gradesService.createGrade(createGradeDto); + } + + // Alle Noten für einen Stundenplan-Eintrag und Woche abrufen + // Lehrer: Alle Noten, Schüler: Nur eigene Noten + @Get('timetable/:timetableEntryId') + async getGradesByTimetableEntry( + @Param('timetableEntryId', ParseIntPipe) timetableEntryId: number, + @Query('weekNumber', ParseIntPipe) weekNumber: number, + @Query('year', ParseIntPipe) year: number, + @Request() req, + ) { + const user = req.user; + + if (user.role === 'Lehrer') { + // Lehrer können alle Noten für diesen Stundenplan-Eintrag und Woche sehen + return this.gradesService.getGradesByTimetableEntry(timetableEntryId, weekNumber, year); + } else { + // Schüler können nur ihre eigenen Noten sehen + return this.gradesService.getStudentGradesByTimetableEntry( + user.id, + timetableEntryId, + weekNumber, + year, + ); + } + } + + // Schüler: Alle eigenen Noten abrufen + // Lehrer: Alle Noten abrufen (optional mit Filter) + @Get() + async getGrades(@Request() req, @Query() query: any) { + const user = req.user; + + if (user.role === 'Lehrer') { + // Lehrer können alle Noten abrufen (optional mit Filter) + const filters: any = {}; + if (query.studentId) filters.studentId = parseInt(query.studentId); + if (query.timetableEntryId) filters.timetableEntryId = parseInt(query.timetableEntryId); + if (query.teacherId) filters.teacherId = query.teacherId; + + return this.gradesService.getAllGrades(filters); + } else { + // Schüler können nur ihre eigenen Noten abrufen + return this.gradesService.getStudentGrades(user.id); + } + } + + // Schüler: Durchschnittsnote für ein Fach + @Get('average/:timetableEntryId') + async getAverageGrade( + @Param('timetableEntryId', ParseIntPipe) timetableEntryId: number, + @Request() req, + ) { + const user = req.user; + + // Nur Schüler können ihre eigene Durchschnittsnote abrufen + if (user.role !== 'Student') { + throw new ForbiddenException('Nur Schüler können Durchschnittsnoten abrufen'); + } + + return this.gradesService.calculateAverageForSubject(user.id, timetableEntryId); + } + + // Schüler: Übersicht über alle Fächer + @Get('overview') + async getStudentOverview(@Request() req) { + const user = req.user; + + // Nur Schüler können ihre Übersicht abrufen + if (user.role !== 'Student') { + throw new ForbiddenException('Nur Schüler können ihre Übersicht abrufen'); + } + + return this.gradesService.getStudentOverview(user.id); + } + + // Lehrer: Komplette Notenübersicht für alle Fächer + @Get('teacher/overview') + @Roles(['Lehrer']) + async getTeacherGradesOverview() { + return this.gradesService.getTeacherGradesOverview(); + } + + // Lehrer: Detaillierte Notenansicht für ein spezifisches Fach (einzelner Stundenplan-Eintrag) + @Get('subject/:timetableEntryId') + @Roles(['Lehrer']) + async getSubjectGradeDetails( + @Param('timetableEntryId', ParseIntPipe) timetableEntryId: number, + ) { + return this.gradesService.getSubjectGradeDetails(timetableEntryId); + } + + // Lehrer: Detaillierte Notenansicht für alle Stundenplan-Einträge mit gleichem Fachnamen + @Get('subject-by-name/:subjectName') + @Roles(['Lehrer']) + async getSubjectGradeDetailsByName( + @Param('subjectName') subjectName: string, + ) { + return this.gradesService.getSubjectGradeDetailsByName(subjectName); + } + + // Lehrer: Note aktualisieren + @Put(':id') + @Roles(['Lehrer']) + async updateGrade( + @Param('id', ParseIntPipe) id: number, + @Body() updateGradeDto: UpdateGradeDto, + ) { + return this.gradesService.updateGrade(id, updateGradeDto); + } + + // Lehrer: Note löschen + @Delete(':id') + @Roles(['Lehrer']) + async deleteGrade(@Param('id', ParseIntPipe) id: number) { + return this.gradesService.deleteGrade(id); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/grades/grades.module.ts b/Smartes-Klassenzimmer-Backend/src/grades/grades.module.ts new file mode 100644 index 0000000..db51c75 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/grades/grades.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { GradesController } from './grades.controller'; +import { GradesService } from './grades.service'; +import { PrismaModule } from '../prisma/prisma.module'; + +@Module({ + imports: [PrismaModule], + controllers: [GradesController], + providers: [GradesService], + exports: [GradesService], +}) +export class GradesModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/grades/grades.service.ts b/Smartes-Klassenzimmer-Backend/src/grades/grades.service.ts new file mode 100644 index 0000000..6f41b8d --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/grades/grades.service.ts @@ -0,0 +1,617 @@ +import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { CreateGradeDto, UpdateGradeDto } from './dto'; + +@Injectable() +export class GradesService { + constructor(private prisma: PrismaService) {} + + // Lehrer: Note erstellen + async createGrade(createGradeDto: CreateGradeDto) { + return this.prisma.grade.create({ + data: { + ...createGradeDto, + date: new Date(createGradeDto.date), + }, + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + }, + }, + }, + }); + } + + // Lehrer: Alle Noten für einen bestimmten Stundenplan-Eintrag und Woche abrufen + async getGradesByTimetableEntry(timetableEntryId: number, weekNumber: number, year: number) { + return this.prisma.grade.findMany({ + where: { + timetableEntryId, + weekNumber, + year, + }, + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + }, + orderBy: [ + { student: { username: 'asc' } }, + ], + }); + } + + // Schüler: Eigene Noten für einen bestimmten Stundenplan-Eintrag und Woche abrufen + async getStudentGradesByTimetableEntry(studentId: number, timetableEntryId: number, weekNumber: number, year: number) { + return this.prisma.grade.findMany({ + where: { + studentId, + timetableEntryId, + weekNumber, + year, + }, + include: { + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + }, + }, + }, + orderBy: { + date: 'desc', + }, + }); + } + + // Schüler: Alle eigenen Noten abrufen + async getStudentGrades(studentId: number) { + return this.prisma.grade.findMany({ + where: { studentId }, + include: { + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + }, + }, + }, + orderBy: { + date: 'desc', + }, + }); + } + + // Lehrer: Alle Noten abrufen (optional mit Filter) + async getAllGrades(filters?: { studentId?: number; timetableEntryId?: number; teacherId?: number }) { + return this.prisma.grade.findMany({ + where: filters, + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + }, + }, + }, + orderBy: { + date: 'desc', + }, + }); + } + + // Lehrer: Note aktualisieren + async updateGrade(id: number, updateGradeDto: UpdateGradeDto) { + const grade = await this.prisma.grade.findUnique({ where: { id } }); + + if (!grade) { + throw new NotFoundException(`Note mit ID ${id} nicht gefunden`); + } + + return this.prisma.grade.update({ + where: { id }, + data: { + ...updateGradeDto, + date: updateGradeDto.date ? new Date(updateGradeDto.date) : undefined, + }, + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + }, + }, + }, + }); + } + + // Lehrer: Note löschen + async deleteGrade(id: number) { + const grade = await this.prisma.grade.findUnique({ where: { id } }); + + if (!grade) { + throw new NotFoundException(`Note mit ID ${id} nicht gefunden`); + } + + return this.prisma.grade.delete({ where: { id } }); + } + + // Schüler: Durchschnittsnote für ein Fach berechnen + async calculateAverageForSubject(studentId: number, timetableEntryId: number) { + const grades = await this.prisma.grade.findMany({ + where: { + studentId, + timetableEntryId, + }, + }); + + if (grades.length === 0) { + return null; + } + + // Gewichtete Durchschnittsnote berechnen + const totalWeight = grades.reduce((sum, g) => sum + (g.weight || 1.0), 0); + const weightedSum = grades.reduce((sum, g) => sum + g.grade * (g.weight || 1.0), 0); + + return { + average: Number((weightedSum / totalWeight).toFixed(2)), + gradeCount: grades.length, + grades: grades.map(g => ({ + id: g.id, + grade: g.grade, + gradeType: g.gradeType, + weight: g.weight, + title: g.title, + date: g.date, + })), + }; + } + + // Schüler: Gesamtübersicht über alle Fächer + async getStudentOverview(studentId: number) { + const grades = await this.prisma.grade.findMany({ + where: { studentId }, + include: { + timetableEntry: { + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + }, + }, + }, + }); + + // Gruppiere Noten nach Fach + const gradesBySubject = grades.reduce((acc, grade) => { + const key = `${grade.timetableEntryId}`; + if (!acc[key]) { + acc[key] = { + timetableEntryId: grade.timetableEntryId, + subject: grade.timetableEntry.subject, + teacher: grade.timetableEntry.teacher, + grades: [], + }; + } + acc[key].grades.push(grade); + return acc; + }, {}); + + // Berechne Durchschnitt für jedes Fach + const overview = Object.values(gradesBySubject).map((subjectData: any) => { + const totalWeight = subjectData.grades.reduce((sum, g) => sum + (g.weight || 1.0), 0); + const weightedSum = subjectData.grades.reduce((sum, g) => sum + g.grade * (g.weight || 1.0), 0); + + return { + timetableEntryId: subjectData.timetableEntryId, + subject: subjectData.subject, + teacher: subjectData.teacher, + average: Number((weightedSum / totalWeight).toFixed(2)), + gradeCount: subjectData.grades.length, + latestGrade: subjectData.grades.sort((a, b) => + new Date(b.date).getTime() - new Date(a.date).getTime() + )[0], + }; + }); + + return overview; + } + + // Lehrer: Komplette Notenübersicht für alle Fächer + async getTeacherGradesOverview() { + // Alle Stundenplan-Einträge mit Noten abrufen + const timetableEntries = await this.prisma.timetableEntry.findMany({ + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + grades: { + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + }, + orderBy: [ + { student: { username: 'asc' } }, + { date: 'desc' }, + ], + }, + }, + orderBy: { + subject: { name: 'asc' }, + }, + }); + + // Gruppiere nach Fachname (subject.name) statt nach timetableEntryId + const subjectGroups = timetableEntries.reduce((acc, entry) => { + const subjectName = entry.subject.name; + + if (!acc[subjectName]) { + acc[subjectName] = { + subject: entry.subject, + timetableEntryIds: [], + entries: [], + allGrades: [], + }; + } + + // Sammle alle timetableEntryIds für dieses Fach + acc[subjectName].timetableEntryIds.push(entry.id); + acc[subjectName].entries.push({ + id: entry.id, + teacher: entry.teacher, + dayOfWeek: entry.dayOfWeek, + startTime: entry.startTime, + endTime: entry.endTime, + }); + + // Sammle alle Noten für dieses Fach + acc[subjectName].allGrades.push(...entry.grades); + + return acc; + }, {}); + + // Berechne Statistiken für jedes gruppierte Fach + return Object.values(subjectGroups).map((subjectData: any) => { + // Gruppiere alle Noten nach Schüler + const gradesByStudent = subjectData.allGrades.reduce((acc, grade) => { + if (!acc[grade.studentId]) { + acc[grade.studentId] = { + student: grade.student, + grades: [], + }; + } + acc[grade.studentId].grades.push(grade); + return acc; + }, {}); + + // Berechne Durchschnitt pro Schüler + const studentAverages = Object.values(gradesByStudent).map((data: any) => { + const totalWeight = data.grades.reduce((sum, g) => sum + (g.weight || 1.0), 0); + const weightedSum = data.grades.reduce((sum, g) => sum + g.grade * (g.weight || 1.0), 0); + const average = totalWeight > 0 ? Number((weightedSum / totalWeight).toFixed(2)) : null; + + return { + student: data.student, + average, + gradeCount: data.grades.length, + grades: data.grades, + }; + }); + + return { + subject: subjectData.subject, + timetableEntryIds: subjectData.timetableEntryIds, // Alle IDs für dieses Fach + timetableEntries: subjectData.entries, // Alle Stundenplan-Einträge für dieses Fach + studentCount: studentAverages.length, + totalGrades: subjectData.allGrades.length, + students: studentAverages, + }; + }).filter(entry => entry.totalGrades > 0); // Nur Fächer mit Noten anzeigen + } + + // Lehrer: Detaillierte Notenansicht für ein spezifisches Fach (einzelner Stundenplan-Eintrag) + async getSubjectGradeDetails(timetableEntryId: number) { + const timetableEntry = await this.prisma.timetableEntry.findUnique({ + where: { id: timetableEntryId }, + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + grades: { + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + }, + orderBy: [ + { student: { username: 'asc' } }, + { date: 'desc' }, + ], + }, + }, + }); + + if (!timetableEntry) { + throw new NotFoundException(`Stundenplan-Eintrag mit ID ${timetableEntryId} nicht gefunden`); + } + + // Gruppiere Noten nach Schüler + const gradesByStudent = timetableEntry.grades.reduce((acc, grade) => { + if (!acc[grade.studentId]) { + acc[grade.studentId] = { + student: grade.student, + grades: [], + }; + } + acc[grade.studentId].grades.push(grade); + return acc; + }, {}); + + // Berechne Durchschnitt und Details pro Schüler + const students = Object.values(gradesByStudent).map((data: any) => { + const totalWeight = data.grades.reduce((sum, g) => sum + (g.weight || 1.0), 0); + const weightedSum = data.grades.reduce((sum, g) => sum + g.grade * (g.weight || 1.0), 0); + const average = totalWeight > 0 ? Number((weightedSum / totalWeight).toFixed(2)) : null; + + return { + student: data.student, + average, + totalWeight, + gradeCount: data.grades.length, + grades: data.grades.map(g => ({ + id: g.id, + grade: g.grade, + gradeType: g.gradeType, + weight: g.weight || 1.0, + title: g.title, + description: g.description, + maxPoints: g.maxPoints, + achievedPoints: g.achievedPoints, + date: g.date, + weekNumber: g.weekNumber, + year: g.year, + })), + }; + }); + + return { + timetableEntry: { + id: timetableEntry.id, + subject: timetableEntry.subject, + teacher: timetableEntry.teacher, + room: timetableEntry.room, + dayOfWeek: timetableEntry.dayOfWeek, + startTime: timetableEntry.startTime, + endTime: timetableEntry.endTime, + }, + students, + totalStudents: students.length, + totalGrades: timetableEntry.grades.length, + }; + } + + // Lehrer: Detaillierte Notenansicht für alle Stundenplan-Einträge mit gleichem Fachnamen + async getSubjectGradeDetailsByName(subjectName: string) { + // Alle Stundenplan-Einträge mit diesem Fachnamen finden + const timetableEntries = await this.prisma.timetableEntry.findMany({ + where: { + subject: { name: subjectName } + }, + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + grades: { + include: { + student: { + select: { + id: true, + username: true, + email: true, + }, + }, + }, + orderBy: [ + { student: { username: 'asc' } }, + { date: 'desc' }, + ], + }, + }, + orderBy: [ + { dayOfWeek: 'asc' }, + { startTime: 'asc' }, + ], + }); + + if (timetableEntries.length === 0) { + throw new NotFoundException(`Keine Stundenplan-Einträge für Fach "${subjectName}" gefunden`); + } + + // Alle Noten über alle Stundenplan-Einträge sammeln + const allGrades = timetableEntries.flatMap(entry => entry.grades); + + // Gruppiere Noten nach Schüler + const gradesByStudent = allGrades.reduce((acc, grade) => { + if (!acc[grade.studentId]) { + acc[grade.studentId] = { + student: grade.student, + grades: [], + }; + } + acc[grade.studentId].grades.push(grade); + return acc; + }, {}); + + // Berechne Durchschnitt und Details pro Schüler + const students = Object.values(gradesByStudent).map((data: any) => { + const totalWeight = data.grades.reduce((sum, g) => sum + (g.weight || 1.0), 0); + const weightedSum = data.grades.reduce((sum, g) => sum + g.grade * (g.weight || 1.0), 0); + const average = totalWeight > 0 ? Number((weightedSum / totalWeight).toFixed(2)) : null; + + return { + student: data.student, + average, + totalWeight, + gradeCount: data.grades.length, + grades: data.grades.map(g => ({ + id: g.id, + grade: g.grade, + gradeType: g.gradeType, + weight: g.weight || 1.0, + title: g.title, + description: g.description, + maxPoints: g.maxPoints, + achievedPoints: g.achievedPoints, + date: g.date, + weekNumber: g.weekNumber, + year: g.year, + })), + }; + }); + + return { + subject: timetableEntries[0].subject, + timetableEntries: timetableEntries.map(entry => ({ + id: entry.id, + teacher: entry.teacher, + room: entry.room, + dayOfWeek: entry.dayOfWeek, + startTime: entry.startTime, + endTime: entry.endTime, + })), + students, + totalStudents: students.length, + totalGrades: allGrades.length, + }; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/iot/iot.controller.ts b/Smartes-Klassenzimmer-Backend/src/iot/iot.controller.ts new file mode 100644 index 0000000..c91b943 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/iot/iot.controller.ts @@ -0,0 +1,93 @@ +import { Controller, Get, Post, Body, Param, ParseIntPipe } from '@nestjs/common'; +import { IotService } from './iot.service'; + +@Controller() +export class IotController { + constructor(private readonly iotService: IotService) {} + + @Get() + getHello(): string { + return this.iotService.getHello(); + } + + @Get('time') + getTime() { + return this.iotService.getTime(); + } + + @Post('scan') + receiveScan(@Body() body: { uid: string; timestamp: number }) { + return this.iotService.receiveScan(body.uid, body.timestamp); + } + + // Student management endpoints + @Post('students') + registerStudent(@Body() body: { firstName: string; lastName: string; className: string; rfidUid: string }) { + return this.iotService.registerStudent(body.firstName, body.lastName, body.className, body.rfidUid); + } + + @Get('students') + getAllStudents() { + return this.iotService.getAllStudents(); + } + + @Get('students/:id') + getStudent(@Param('id', ParseIntPipe) id: number) { + return this.iotService.getStudent(id); + } + + @Post('students/:id/assign-card') + assignCard(@Param('id', ParseIntPipe) id: number, @Body() body: { rfidUid: string }) { + return this.iotService.assignCard(id, body.rfidUid); + } + + @Post('students/:id/update') + updateStudent( + @Param('id', ParseIntPipe) id: number, + @Body() body: { firstName?: string; lastName?: string; className?: string; rfidUid?: string } + ) { + return this.iotService.updateStudent(id, body.firstName, body.lastName, body.className, body.rfidUid); + } + + @Post('students/:id/card-active') + setCardActive(@Param('id', ParseIntPipe) id: number, @Body() body: { isActive: boolean }) { + return this.iotService.setCardActive(id, body.isActive); + } + + @Post('students/:id/delete') + deleteStudent(@Param('id', ParseIntPipe) id: number) { + return this.iotService.deleteStudent(id); + } + + // Attendance endpoints + @Get('attendance/present') + getCurrentlyPresent() { + return this.iotService.getCurrentlyPresent(); + } + + @Get('attendance/date/:date') + getScansForDate(@Param('date') date: string) { + return this.iotService.getScansForDate(date); + } + + @Get('attendance/student/:id') + getStudentAttendance(@Param('id', ParseIntPipe) id: number) { + return this.iotService.getStudentAttendance(id); + } + + @Get('attendance/all') + getAllAttendanceLogs() { + return this.iotService.getAllAttendanceLogs(); + } + + // Admin endpoints + @Post('admin/attendance/manual-entry') + addManualEntry(@Body() body: { studentId: number; scannedAt: string; eventType: string; reason: string }) { + return this.iotService.addManualEntry(body.studentId, new Date(body.scannedAt), body.eventType, body.reason); + } + + @Post('admin/attendance/log/:logId/delete') + deleteAttendanceLog(@Param('logId', ParseIntPipe) logId: number) { + return this.iotService.deleteAttendanceLog(logId); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/iot/iot.module.ts b/Smartes-Klassenzimmer-Backend/src/iot/iot.module.ts new file mode 100644 index 0000000..198c46a --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/iot/iot.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { IotController } from './iot.controller'; +import { IotService } from './iot.service'; +import { PrismaModule } from '../prisma/prisma.module'; +import { ScheduleModule } from '@nestjs/schedule'; + +@Module({ + imports: [PrismaModule, ScheduleModule], + controllers: [IotController], + providers: [IotService], +}) +export class IotModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/iot/iot.service.ts b/Smartes-Klassenzimmer-Backend/src/iot/iot.service.ts new file mode 100644 index 0000000..2fa77ba --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/iot/iot.service.ts @@ -0,0 +1,461 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { Cron } from '@nestjs/schedule'; + +@Injectable() +export class IotService { + constructor(private prisma: PrismaService) {} + + getHello(): string { + return 'Hello World!'; + } + + getTime() { + // Get current time in Berlin timezone + const now = new Date(); + const berlinTime = new Date(now.toLocaleString('en-US', { timeZone: 'Europe/Berlin' })); + const unixTimestamp = Math.floor(berlinTime.getTime() / 1000); + + return { + unix: unixTimestamp, + readable: berlinTime.toLocaleString('de-DE', { timeZone: 'Europe/Berlin' }) + }; + } + + async receiveScan(uid: string, timestamp: number) { + const scanTime = new Date(timestamp * 1000); + + const berlinTime = scanTime.toLocaleString('de-DE', { + timeZone: 'Europe/Berlin', + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit' + }); + + // Find student by RFID UID + const student = await this.prisma.student.findUnique({ + where: { studentRfidCardUid: uid } + }); + + // Check if card is unregistered or disabled + if (!student) { + console.log(`[RFID Scan] UID: ${uid} | Time: ${berlinTime} | WARNING: Unregistered card`); + return 'BLOCKED'; + } + + if (!student.studentCardIsActive) { + console.log(`[RFID Scan] ${student.studentFirstName} ${student.studentLastName} | WARNING: Card disabled`); + return 'BLOCKED'; + } + + // Use transaction to prevent race conditions during rapid cached uploads + const result = await this.prisma.$transaction(async (tx) => { + // Get last scan (any day) with row lock + const lastScan = await tx.attendanceLog.findFirst({ + where: { studentId: student.studentId }, + orderBy: { attendanceScannedAt: 'desc' } + }); + + let eventType: string; + + if (!lastScan) { + // First scan ever = IN + eventType = 'IN'; + } else { + // Convert both times to Berlin timezone for comparison + const lastScanBerlin = new Date(lastScan.attendanceScannedAt.toLocaleString('en-US', { timeZone: 'Europe/Berlin' })); + const currentBerlin = new Date(scanTime.toLocaleString('en-US', { timeZone: 'Europe/Berlin' })); + + console.log(`[Debug] Last scan: ${lastScanBerlin.toISOString()} | Current: ${currentBerlin.toISOString()}`); + console.log(`[Debug] Last event type: ${lastScan.attendanceEventType}`); + + // Check if last scan was on a different day (in Berlin timezone) + const isDifferentDay = + lastScanBerlin.getDate() !== currentBerlin.getDate() || + lastScanBerlin.getMonth() !== currentBerlin.getMonth() || + lastScanBerlin.getFullYear() !== currentBerlin.getFullYear(); + + console.log(`[Debug] Is different day? ${isDifferentDay}`); + + if (isDifferentDay) { + // Different day = always start with IN + eventType = 'IN'; + + // Auto-close previous day if still IN + if (lastScan.attendanceEventType === 'IN') { + const endOfLastDay = new Date(lastScanBerlin); + endOfLastDay.setHours(23, 59, 59, 999); + + await tx.attendanceLog.create({ + data: { + studentId: student.studentId, + attendanceScannedAt: endOfLastDay, + attendanceEventType: 'OUT', + wasManualEntry: true, + manualEntryReason: 'Auto-closed: forgot to sign out' + } + }); + + console.log(`[Auto Close] ${student.studentFirstName} ${student.studentLastName} at 23:59:59`); + } + } else { + // Same day = toggle + eventType = lastScan.attendanceEventType === 'IN' ? 'OUT' : 'IN'; + } + } + + // Save scan within transaction + await tx.attendanceLog.create({ + data: { + studentId: student.studentId, + attendanceScannedAt: scanTime, + attendanceEventType: eventType, + wasManualEntry: false + } + }); + + return eventType; + }); + + const eventType = result; + + const action = eventType === 'IN' ? 'SIGN IN' : 'SIGN OUT'; + const studentName = `${student.studentFirstName} ${student.studentLastName}`; + + console.log(`[Attendance] ${studentName} | ${action} | Time: ${berlinTime} | Saved to DB`); + + // Return specific response for ESP32 + return eventType === 'IN' ? 'SIGNIN' : 'SIGNOUT'; + } + + // Register new student + async registerStudent(firstName: string, lastName: string, className: string, rfidUid: string) { + const student = await this.prisma.student.create({ + data: { + studentFirstName: firstName, + studentLastName: lastName, + studentClassName: className, + studentRfidCardUid: rfidUid, + studentCardIsActive: true + } + }); + + console.log(`[Student] Registered: ${firstName} ${lastName} (${className}) with card ${rfidUid}`); + + return student; + } + + // Get all students + async getAllStudents() { + return await this.prisma.student.findMany({ + orderBy: { studentLastName: 'asc' } + }); + } + + // Get student by ID + async getStudent(id: number) { + return await this.prisma.student.findUnique({ + where: { studentId: id }, + include: { + attendanceLogs: { + orderBy: { attendanceScannedAt: 'desc' }, + take: 20 + } + } + }); + } + + // Update student RFID card + async assignCard(id: number, rfidUid: string) { + const student = await this.prisma.student.update({ + where: { studentId: id }, + data: { studentRfidCardUid: rfidUid } + }); + + console.log(`[Student] Assigned card ${rfidUid} to ${student.studentFirstName} ${student.studentLastName}`); + + return student; + } + + // Update student information + async updateStudent(id: number, firstName?: string, lastName?: string, className?: string, rfidUid?: string) { + const data: any = {}; + if (firstName) data.studentFirstName = firstName; + if (lastName) data.studentLastName = lastName; + if (className) data.studentClassName = className; + if (rfidUid) data.studentRfidCardUid = rfidUid; + + const student = await this.prisma.student.update({ + where: { studentId: id }, + data + }); + + console.log(`[Student] Updated: ${student.studentFirstName} ${student.studentLastName}`); + + return student; + } + + // Enable/Disable student card + async setCardActive(id: number, isActive: boolean) { + const student = await this.prisma.student.update({ + where: { studentId: id }, + data: { studentCardIsActive: isActive } + }); + + const status = isActive ? 'enabled' : 'disabled'; + console.log(`[Student] Card ${status} for ${student.studentFirstName} ${student.studentLastName}`); + + return student; + } + + // Delete student and all attendance logs + async deleteStudent(id: number) { + const student = await this.prisma.student.delete({ + where: { studentId: id } + }); + + console.log(`[Student] Deleted: ${student.studentFirstName} ${student.studentLastName} and all attendance logs`); + + return student; + } + + // Get currently present students + async getCurrentlyPresent() { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + // Get all active students with their last scan of the day + const students = await this.prisma.student.findMany({ + where: { studentCardIsActive: true }, + include: { + attendanceLogs: { + where: { + attendanceScannedAt: { gte: today } + }, + orderBy: { attendanceScannedAt: 'desc' }, + take: 1 + } + } + }); + + // Filter to only those whose last scan was IN + const present = students + .filter(s => s.attendanceLogs.length > 0 && s.attendanceLogs[0].attendanceEventType === 'IN') + .map(s => ({ + studentId: s.studentId, + name: `${s.studentFirstName} ${s.studentLastName}`, + className: s.studentClassName, + checkInTime: s.attendanceLogs[0].attendanceScannedAt, + minutesPresent: Math.floor((Date.now() - s.attendanceLogs[0].attendanceScannedAt.getTime()) / 60000) + })); + + // Get absent students (no scans today) + const absent = students + .filter(s => s.attendanceLogs.length === 0) + .map(s => ({ + studentId: s.studentId, + name: `${s.studentFirstName} ${s.studentLastName}`, + className: s.studentClassName + })); + + return { + date: new Date().toISOString().split('T')[0], + time: new Date().toLocaleTimeString('de-DE'), + presentStudents: present, + absentStudents: absent, + totalPresent: present.length, + totalAbsent: absent.length + }; + } + + // Get all scans for a specific date + async getScansForDate(dateString: string) { + const date = new Date(dateString); + date.setHours(0, 0, 0, 0); + + const nextDay = new Date(date); + nextDay.setDate(nextDay.getDate() + 1); + + const logs = await this.prisma.attendanceLog.findMany({ + where: { + attendanceScannedAt: { + gte: date, + lt: nextDay + } + }, + include: { + student: true + }, + orderBy: { attendanceScannedAt: 'asc' } + }); + + // Group by student + const studentLogs = new Map(); + + logs.forEach(log => { + const studentId = log.studentId; + if (!studentLogs.has(studentId)) { + studentLogs.set(studentId, { + studentId: studentId, + name: `${log.student.studentFirstName} ${log.student.studentLastName}`, + className: log.student.studentClassName, + logs: [] + }); + } + studentLogs.get(studentId).logs.push({ + time: log.attendanceScannedAt, + action: log.attendanceEventType, + wasManual: log.wasManualEntry, + reason: log.manualEntryReason + }); + }); + + return { + date: dateString, + students: Array.from(studentLogs.values()) + }; + } + + // Get all attendance logs for a specific student + async getStudentAttendance(id: number) { + const student = await this.prisma.student.findUnique({ + where: { studentId: id }, + include: { + attendanceLogs: { + orderBy: { attendanceScannedAt: 'desc' } + } + } + }); + + if (!student) { + throw new Error('Student not found'); + } + + return { + studentId: student.studentId, + name: `${student.studentFirstName} ${student.studentLastName}`, + className: student.studentClassName, + logs: student.attendanceLogs.map(log => ({ + id: log.attendanceLogId, + time: log.attendanceScannedAt, + action: log.attendanceEventType, + wasManual: log.wasManualEntry, + reason: log.manualEntryReason + })) + }; + } + + // Get all attendance logs (for sorting/filtering) + async getAllAttendanceLogs() { + const logs = await this.prisma.attendanceLog.findMany({ + include: { + student: true + }, + orderBy: { attendanceScannedAt: 'desc' } + }); + + return logs.map(log => ({ + id: log.attendanceLogId, + studentId: log.studentId, + studentName: `${log.student.studentFirstName} ${log.student.studentLastName}`, + className: log.student.studentClassName, + time: log.attendanceScannedAt, + action: log.attendanceEventType, + wasManual: log.wasManualEntry, + reason: log.manualEntryReason + })); + } + + // Admin: Manual attendance entry + async addManualEntry(studentId: number, scannedAt: Date, eventType: string, reason: string) { + const student = await this.prisma.student.findUnique({ + where: { studentId } + }); + + if (!student) { + throw new Error('Student not found'); + } + + const log = await this.prisma.attendanceLog.create({ + data: { + studentId, + attendanceScannedAt: scannedAt, + attendanceEventType: eventType, + wasManualEntry: true, + manualEntryReason: reason + } + }); + + console.log(`[Manual Entry] ${student.studentFirstName} ${student.studentLastName} | ${eventType} | ${reason}`); + return log; + } + + // Admin: Delete attendance log + async deleteAttendanceLog(logId: number) { + const log = await this.prisma.attendanceLog.delete({ + where: { attendanceLogId: logId } + }); + + console.log(`[Delete Log] ID ${logId} deleted`); + + return log; + } + + // Cron job: Auto sign-out at midnight + @Cron('0 0 * * *') // Runs at 00:00 every day + async autoSignOutAtMidnight() { + console.log('[Cron] Running auto sign-out at midnight...'); + + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + yesterday.setHours(0, 0, 0, 0); + + const yesterdayEnd = new Date(yesterday); + yesterdayEnd.setHours(23, 59, 59, 999); + + const midnight = new Date(); + midnight.setHours(0, 0, 0, 0); + + // Find all students who signed in yesterday but never signed out + const students = await this.prisma.student.findMany({ + include: { + attendanceLogs: { + where: { + attendanceScannedAt: { gte: yesterday, lt: midnight } + }, + orderBy: { attendanceScannedAt: 'desc' } + } + } + }); + + let autoSignedOut = 0; + + for (const student of students) { + if (student.attendanceLogs.length > 0) { + const lastLog = student.attendanceLogs[0]; + + // If last log was IN (not signed out) + if (lastLog.attendanceEventType === 'IN') { + // Create auto sign-out at 23:59:59 + await this.prisma.attendanceLog.create({ + data: { + studentId: student.studentId, + attendanceScannedAt: yesterdayEnd, + attendanceEventType: 'OUT', + wasManualEntry: true, + manualEntryReason: 'Auto-closed at midnight' + } + }); + + autoSignedOut++; + console.log(`[Auto Sign-Out] ${student.studentFirstName} ${student.studentLastName} at 23:59:59`); + } + } + } + + console.log(`[Cron] Auto signed out ${autoSignedOut} students`); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/mail/mail.module.ts b/Smartes-Klassenzimmer-Backend/src/mail/mail.module.ts new file mode 100644 index 0000000..30d33f0 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/mail/mail.module.ts @@ -0,0 +1,38 @@ +import { Module } from '@nestjs/common'; +import { MailerModule } from '@nestjs-modules/mailer'; +import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter'; +import { join } from 'path'; +import { MailService } from './mail.service'; +import { ConfigService } from '@nestjs/config'; + +@Module({ + imports: [ + MailerModule.forRootAsync({ + useFactory: async (config: ConfigService) => ({ + transport: { + host: config.get('SMTP_HOST'), + port: config.get('SMTP_PORT'), + secure: false, // true for 465, false for other ports + auth: { + user: config.get('SMTP_USER'), + pass: config.get('SMTP_PASS'), + }, + }, + defaults: { + from: `"Smartes Klassenzimmer" <${config.get('SMTP_FROM')}>`, + }, + template: { + dir: join(__dirname, 'templates'), + adapter: new HandlebarsAdapter(), + options: { + strict: true, + }, + }, + }), + inject: [ConfigService], + }), + ], + providers: [MailService], + exports: [MailService], +}) +export class MailModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/mail/mail.service.ts b/Smartes-Klassenzimmer-Backend/src/mail/mail.service.ts new file mode 100644 index 0000000..1d4681d --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/mail/mail.service.ts @@ -0,0 +1,38 @@ +import { MailerService } from '@nestjs-modules/mailer'; +import { Injectable } from '@nestjs/common'; +import { User } from '@prisma/client'; + +@Injectable() +export class MailService { + constructor(private mailerService: MailerService) {} + + async sendUserConfirmation(user: User) { + // For now, we don't have a confirmation token flow, just a welcome email + // If we wanted confirmation, we'd generate a token here or pass it in. + + await this.mailerService.sendMail({ + to: user.email, + // from: '"Support Team" ', // override default from + subject: 'Willkommen im Smarten Klassenzimmer!', + template: './confirmation', // `.hbs` extension is appended automatically + context: { + name: user.username, + // url, + }, + }); + } + + async sendPasswordReset(user: User, token: string) { + const url = `http://localhost:5173/reset-password?token=${token}`; // Frontend URL + + await this.mailerService.sendMail({ + to: user.email, + subject: 'Passwort zurücksetzen', + template: './reset-password', + context: { + name: user.username, + url, + }, + }); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/mail/templates/confirmation.hbs b/Smartes-Klassenzimmer-Backend/src/mail/templates/confirmation.hbs new file mode 100644 index 0000000..f605854 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/mail/templates/confirmation.hbs @@ -0,0 +1,5 @@ +

Hey {{ name }},

+

Willkommen im Smarten Klassenzimmer!

+

Deine Registrierung war erfolgreich.

+ +

Viel Spaß beim Lernen!

diff --git a/Smartes-Klassenzimmer-Backend/src/mail/templates/reset-password.hbs b/Smartes-Klassenzimmer-Backend/src/mail/templates/reset-password.hbs new file mode 100644 index 0000000..d5a60a1 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/mail/templates/reset-password.hbs @@ -0,0 +1,8 @@ +

Hey {{ name }},

+

Du hast angefordert, dein Passwort zurückzusetzen.

+

Klicke auf den folgenden Link, um ein neues Passwort zu setzen:

+

+ Passwort zurücksetzen +

+ +

Wenn du das nicht angefordert hast, ignoriere diese Email einfach.

diff --git a/Smartes-Klassenzimmer-Backend/src/main.ts b/Smartes-Klassenzimmer-Backend/src/main.ts new file mode 100644 index 0000000..0ce2f79 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/main.ts @@ -0,0 +1,41 @@ +import { NestFactory } from '@nestjs/core'; +import { AppModule } from './app.module'; +import { NestExpressApplication } from '@nestjs/platform-express'; +import { ValidationPipe } from '@nestjs/common'; +import cookieParser from 'cookie-parser'; + +async function bootstrap() { + const app = await NestFactory.create(AppModule); + + // CORS Konfiguration + app.enableCors({ + origin: [ + 'http://localhost:5173', // Vite Dev Server + 'http://127.0.0.1:5173', // Vite Dev Server (Alternative) + 'http://localhost:5500', + 'http://127.0.0.1:5500', + 'http://localhost:3000' + ], + credentials: true, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization'], + }); + + // Globales Präfix für alle Routes + app.setGlobalPrefix('api'); + + // Cookie Parser für JWT-Cookies + app.use(cookieParser()); + + // Validation Pipe für DTOs + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + forbidNonWhitelisted: true, + transform: true, + }), + ); + + await app.listen(process.env.PORT ?? 3000); +} +bootstrap(); diff --git a/Smartes-Klassenzimmer-Backend/src/prisma/prisma.module.ts b/Smartes-Klassenzimmer-Backend/src/prisma/prisma.module.ts new file mode 100644 index 0000000..7207426 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/prisma/prisma.module.ts @@ -0,0 +1,9 @@ +import { Global, Module } from '@nestjs/common'; +import { PrismaService } from './prisma.service'; + +@Global() +@Module({ + providers: [PrismaService], + exports: [PrismaService], +}) +export class PrismaModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/prisma/prisma.service.ts b/Smartes-Klassenzimmer-Backend/src/prisma/prisma.service.ts new file mode 100644 index 0000000..32cbe1a --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/prisma/prisma.service.ts @@ -0,0 +1,15 @@ +import { Injectable, OnModuleInit } from '@nestjs/common'; +import { PrismaClient } from '@prisma/client'; + +@Injectable() +export class PrismaService extends PrismaClient implements OnModuleInit { + async onModuleInit() { + await this.$connect(); + } + + async enableShutdownHooks(app: any) { + process.on('beforeExit', async () => { + await app.close(); + }); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/rooms/dto/create-room.dto.ts b/Smartes-Klassenzimmer-Backend/src/rooms/dto/create-room.dto.ts new file mode 100644 index 0000000..89af61b --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/rooms/dto/create-room.dto.ts @@ -0,0 +1,19 @@ +import { IsString, IsOptional, IsInt, Min } from 'class-validator'; + +export class CreateRoomDto { + @IsString() + number: string; // z.B. "A101", "B203" + + @IsOptional() + @IsString() + building?: string; // z.B. "Hauptgebäude", "Neubau" + + @IsOptional() + @IsInt() + @Min(1, { message: 'capacity muss mindestens 1 sein' }) + capacity?: number; // Anzahl Sitzplätze + + @IsOptional() + @IsString() + equipment?: string; // z.B. "Beamer, Whiteboard" +} diff --git a/Smartes-Klassenzimmer-Backend/src/rooms/dto/index.ts b/Smartes-Klassenzimmer-Backend/src/rooms/dto/index.ts new file mode 100644 index 0000000..0c46785 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/rooms/dto/index.ts @@ -0,0 +1,2 @@ +export * from './create-room.dto'; +export * from './update-room.dto'; diff --git a/Smartes-Klassenzimmer-Backend/src/rooms/dto/update-room.dto.ts b/Smartes-Klassenzimmer-Backend/src/rooms/dto/update-room.dto.ts new file mode 100644 index 0000000..be0053c --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/rooms/dto/update-room.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateRoomDto } from './create-room.dto'; + +export class UpdateRoomDto extends PartialType(CreateRoomDto) {} diff --git a/Smartes-Klassenzimmer-Backend/src/rooms/rooms.controller.ts b/Smartes-Klassenzimmer-Backend/src/rooms/rooms.controller.ts new file mode 100644 index 0000000..7982a25 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/rooms/rooms.controller.ts @@ -0,0 +1,60 @@ +import { + Controller, + Get, + Post, + Put, + Delete, + Body, + Param, + UseGuards, + ParseIntPipe, +} from '@nestjs/common'; +import { RoomsService } from './rooms.service'; +import { JwtAuthGuard, RolesGuard } from '../auth/guards'; +import { Roles } from '../auth/guards/roles.guard'; +import { CreateRoomDto, UpdateRoomDto } from './dto'; + +@Controller('rooms') +@UseGuards(JwtAuthGuard) +export class RoomsController { + constructor(private readonly roomsService: RoomsService) {} + + // GET /rooms - Alle Räume abrufen + @Get() + async findAll() { + return this.roomsService.findAll(); + } + + // GET /rooms/:id - Einen Raum nach ID abrufen + @Get(':id') + async findOne(@Param('id', ParseIntPipe) id: number) { + return this.roomsService.findOne(id); + } + + // POST /rooms - Neuen Raum erstellen (nur Lehrer) + @Post() + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + async create(@Body() createDto: CreateRoomDto) { + return this.roomsService.create(createDto); + } + + // PUT /rooms/:id - Raum aktualisieren (nur Lehrer) + @Put(':id') + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + async update( + @Param('id', ParseIntPipe) id: number, + @Body() updateDto: UpdateRoomDto, + ) { + return this.roomsService.update(id, updateDto); + } + + // DELETE /rooms/:id - Raum löschen (nur Lehrer) + @Delete(':id') + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + async delete(@Param('id', ParseIntPipe) id: number) { + return this.roomsService.delete(id); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/rooms/rooms.module.ts b/Smartes-Klassenzimmer-Backend/src/rooms/rooms.module.ts new file mode 100644 index 0000000..6f75aae --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/rooms/rooms.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { RoomsController } from './rooms.controller'; +import { RoomsService } from './rooms.service'; +import { PrismaModule } from '../prisma/prisma.module'; + +@Module({ + imports: [PrismaModule], + controllers: [RoomsController], + providers: [RoomsService], + exports: [RoomsService], +}) +export class RoomsModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/rooms/rooms.service.ts b/Smartes-Klassenzimmer-Backend/src/rooms/rooms.service.ts new file mode 100644 index 0000000..2a4305b --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/rooms/rooms.service.ts @@ -0,0 +1,86 @@ +import { Injectable, NotFoundException, ConflictException } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { CreateRoomDto, UpdateRoomDto } from './dto'; + +@Injectable() +export class RoomsService { + constructor(private prisma: PrismaService) {} + + // Alle Räume abrufen + async findAll() { + return this.prisma.room.findMany({ + orderBy: { number: 'asc' }, + include: { + _count: { + select: { timetableEntries: true }, + }, + }, + }); + } + + // Einen Raum nach ID abrufen + async findOne(id: number) { + const room = await this.prisma.room.findUnique({ + where: { id }, + include: { + _count: { + select: { timetableEntries: true }, + }, + }, + }); + + if (!room) { + throw new NotFoundException(`Raum mit ID ${id} nicht gefunden`); + } + + return room; + } + + // Neuen Raum erstellen (nur Lehrer) + async create(dto: CreateRoomDto) { + try { + return await this.prisma.room.create({ + data: dto, + }); + } catch (error) { + if (error.code === 'P2002') { + throw new ConflictException('Ein Raum mit dieser Nummer existiert bereits'); + } + throw error; + } + } + + // Raum aktualisieren (nur Lehrer) + async update(id: number, dto: UpdateRoomDto) { + await this.findOne(id); // Prüfen, ob Raum existiert + + try { + return await this.prisma.room.update({ + where: { id }, + data: dto, + }); + } catch (error) { + if (error.code === 'P2002') { + throw new ConflictException('Ein Raum mit dieser Nummer existiert bereits'); + } + throw error; + } + } + + // Raum löschen (nur Lehrer) + async delete(id: number) { + await this.findOne(id); // Prüfen, ob Raum existiert + + try { + await this.prisma.room.delete({ + where: { id }, + }); + return { message: 'Raum erfolgreich gelöscht' }; + } catch (error) { + if (error.code === 'P2003') { + throw new ConflictException('Raum kann nicht gelöscht werden, da er noch in Stundenplan-Einträgen verwendet wird'); + } + throw error; + } + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/subjects/dto/create-subject.dto.ts b/Smartes-Klassenzimmer-Backend/src/subjects/dto/create-subject.dto.ts new file mode 100644 index 0000000..a14ff94 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/subjects/dto/create-subject.dto.ts @@ -0,0 +1,17 @@ +import { IsString, IsOptional, Matches } from 'class-validator'; + +export class CreateSubjectDto { + @IsString() + name: string; // z.B. "Mathematik", "Deutsch", "Englisch" + + @IsOptional() + @IsString() + abbreviation?: string; // z.B. "Mathe", "DE", "EN" + + @IsOptional() + @IsString() + @Matches(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/, { + message: 'color muss ein gültiger Hex-Code sein (z.B. #FF5733)', + }) + color?: string; // z.B. "#FF5733" +} diff --git a/Smartes-Klassenzimmer-Backend/src/subjects/dto/index.ts b/Smartes-Klassenzimmer-Backend/src/subjects/dto/index.ts new file mode 100644 index 0000000..d5a0bc6 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/subjects/dto/index.ts @@ -0,0 +1,2 @@ +export * from './create-subject.dto'; +export * from './update-subject.dto'; diff --git a/Smartes-Klassenzimmer-Backend/src/subjects/dto/update-subject.dto.ts b/Smartes-Klassenzimmer-Backend/src/subjects/dto/update-subject.dto.ts new file mode 100644 index 0000000..d347ac3 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/subjects/dto/update-subject.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateSubjectDto } from './create-subject.dto'; + +export class UpdateSubjectDto extends PartialType(CreateSubjectDto) {} diff --git a/Smartes-Klassenzimmer-Backend/src/subjects/subjects.controller.ts b/Smartes-Klassenzimmer-Backend/src/subjects/subjects.controller.ts new file mode 100644 index 0000000..e1a3826 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/subjects/subjects.controller.ts @@ -0,0 +1,60 @@ +import { + Controller, + Get, + Post, + Put, + Delete, + Body, + Param, + UseGuards, + ParseIntPipe, +} from '@nestjs/common'; +import { SubjectsService } from './subjects.service'; +import { JwtAuthGuard, RolesGuard } from '../auth/guards'; +import { Roles } from '../auth/guards/roles.guard'; +import { CreateSubjectDto, UpdateSubjectDto } from './dto'; + +@Controller('subjects') +@UseGuards(JwtAuthGuard) +export class SubjectsController { + constructor(private readonly subjectsService: SubjectsService) {} + + // GET /subjects - Alle Fächer abrufen + @Get() + async findAll() { + return this.subjectsService.findAll(); + } + + // GET /subjects/:id - Ein Fach nach ID abrufen + @Get(':id') + async findOne(@Param('id', ParseIntPipe) id: number) { + return this.subjectsService.findOne(id); + } + + // POST /subjects - Neues Fach erstellen (nur Lehrer) + @Post() + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + async create(@Body() createDto: CreateSubjectDto) { + return this.subjectsService.create(createDto); + } + + // PUT /subjects/:id - Fach aktualisieren (nur Lehrer) + @Put(':id') + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + async update( + @Param('id', ParseIntPipe) id: number, + @Body() updateDto: UpdateSubjectDto, + ) { + return this.subjectsService.update(id, updateDto); + } + + // DELETE /subjects/:id - Fach löschen (nur Lehrer) + @Delete(':id') + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + async delete(@Param('id', ParseIntPipe) id: number) { + return this.subjectsService.delete(id); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/subjects/subjects.module.ts b/Smartes-Klassenzimmer-Backend/src/subjects/subjects.module.ts new file mode 100644 index 0000000..4dfb18c --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/subjects/subjects.module.ts @@ -0,0 +1,12 @@ +import { Module } from '@nestjs/common'; +import { SubjectsController } from './subjects.controller'; +import { SubjectsService } from './subjects.service'; +import { PrismaModule } from '../prisma/prisma.module'; + +@Module({ + imports: [PrismaModule], + controllers: [SubjectsController], + providers: [SubjectsService], + exports: [SubjectsService], +}) +export class SubjectsModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/subjects/subjects.service.ts b/Smartes-Klassenzimmer-Backend/src/subjects/subjects.service.ts new file mode 100644 index 0000000..6617b84 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/subjects/subjects.service.ts @@ -0,0 +1,86 @@ +import { Injectable, NotFoundException, ConflictException } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { CreateSubjectDto, UpdateSubjectDto } from './dto'; + +@Injectable() +export class SubjectsService { + constructor(private prisma: PrismaService) {} + + // Alle Fächer abrufen + async findAll() { + return this.prisma.subject.findMany({ + orderBy: { name: 'asc' }, + include: { + _count: { + select: { timetableEntries: true }, + }, + }, + }); + } + + // Ein Fach nach ID abrufen + async findOne(id: number) { + const subject = await this.prisma.subject.findUnique({ + where: { id }, + include: { + _count: { + select: { timetableEntries: true }, + }, + }, + }); + + if (!subject) { + throw new NotFoundException(`Fach mit ID ${id} nicht gefunden`); + } + + return subject; + } + + // Neues Fach erstellen (nur Lehrer) + async create(dto: CreateSubjectDto) { + try { + return await this.prisma.subject.create({ + data: dto, + }); + } catch (error) { + if (error.code === 'P2002') { + throw new ConflictException('Ein Fach mit diesem Namen existiert bereits'); + } + throw error; + } + } + + // Fach aktualisieren (nur Lehrer) + async update(id: number, dto: UpdateSubjectDto) { + await this.findOne(id); // Prüfen, ob Fach existiert + + try { + return await this.prisma.subject.update({ + where: { id }, + data: dto, + }); + } catch (error) { + if (error.code === 'P2002') { + throw new ConflictException('Ein Fach mit diesem Namen existiert bereits'); + } + throw error; + } + } + + // Fach löschen (nur Lehrer) + async delete(id: number) { + await this.findOne(id); // Prüfen, ob Fach existiert + + try { + await this.prisma.subject.delete({ + where: { id }, + }); + return { message: 'Fach erfolgreich gelöscht' }; + } catch (error) { + if (error.code === 'P2003') { + throw new ConflictException('Fach kann nicht gelöscht werden, da es noch in Stundenplan-Einträgen verwendet wird'); + } + throw error; + } + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/timetable/dto/create-timetable-entry.dto.ts b/Smartes-Klassenzimmer-Backend/src/timetable/dto/create-timetable-entry.dto.ts new file mode 100644 index 0000000..d8ebd21 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/timetable/dto/create-timetable-entry.dto.ts @@ -0,0 +1,68 @@ +import { + IsInt, + IsString, + IsOptional, + IsBoolean, + Min, + Max, + Matches, +} from 'class-validator'; + +export class CreateTimetableEntryDto { + @IsInt() + @Min(1, { message: 'dayOfWeek muss zwischen 1 (Montag) und 5 (Freitag) sein' }) + @Max(5, { message: 'dayOfWeek muss zwischen 1 (Montag) und 5 (Freitag) sein' }) + dayOfWeek: number; + + @IsString() + @Matches(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/, { + message: 'startTime muss im Format HH:MM sein (z.B. 08:00)', + }) + startTime: string; + + @IsString() + @Matches(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/, { + message: 'endTime muss im Format HH:MM sein (z.B. 09:30)', + }) + endTime: string; + + @IsOptional() + @IsInt() + subjectId?: number; // Foreign Key zu Subject + + @IsOptional() + @IsString() + subjectName?: string; // Alternativ: Name des Fachs + + @IsOptional() + @IsInt() + teacherId?: number; // Foreign Key zu User (Lehrer) + + @IsOptional() + @IsInt() + roomId?: number; // Foreign Key zu Room + + @IsOptional() + @IsString() + roomNumber?: string; // Alternativ: Raumnummer + + @IsOptional() + @IsInt() + @Min(1, { message: 'weekNumber muss zwischen 1 und 53 sein' }) + @Max(53, { message: 'weekNumber muss zwischen 1 und 53 sein' }) + weekNumber?: number; + + @IsOptional() + @IsInt() + @Min(2020, { message: 'year muss ein gültiges Jahr sein' }) + @Max(2100, { message: 'year muss ein gültiges Jahr sein' }) + year?: number; + + @IsOptional() + @IsBoolean() + isRecurring?: boolean; + + @IsOptional() + @IsBoolean() + allowStudentUploads?: boolean; +} diff --git a/Smartes-Klassenzimmer-Backend/src/timetable/dto/index.ts b/Smartes-Klassenzimmer-Backend/src/timetable/dto/index.ts new file mode 100644 index 0000000..37ce9ac --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/timetable/dto/index.ts @@ -0,0 +1,2 @@ +export * from './create-timetable-entry.dto'; +export * from './update-timetable-entry.dto'; diff --git a/Smartes-Klassenzimmer-Backend/src/timetable/dto/update-timetable-entry.dto.ts b/Smartes-Klassenzimmer-Backend/src/timetable/dto/update-timetable-entry.dto.ts new file mode 100644 index 0000000..b6be379 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/timetable/dto/update-timetable-entry.dto.ts @@ -0,0 +1,6 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateTimetableEntryDto } from './create-timetable-entry.dto'; + +export class UpdateTimetableEntryDto extends PartialType( + CreateTimetableEntryDto, +) {} diff --git a/Smartes-Klassenzimmer-Backend/src/timetable/timetable.controller.ts b/Smartes-Klassenzimmer-Backend/src/timetable/timetable.controller.ts new file mode 100644 index 0000000..7870834 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/timetable/timetable.controller.ts @@ -0,0 +1,60 @@ +import { + Controller, + Get, + Post, + Put, + Delete, + Body, + Param, + Query, + UseGuards, + ParseIntPipe, +} from '@nestjs/common'; +import { TimetableService } from './timetable.service'; +import { JwtAuthGuard, RolesGuard } from '../auth/guards'; +import { Roles } from '../auth/guards/roles.guard'; +import { CreateTimetableEntryDto, UpdateTimetableEntryDto } from './dto'; + +@Controller('timetable') +@UseGuards(JwtAuthGuard) +export class TimetableController { + constructor(private readonly timetableService: TimetableService) {} + + // GET /timetable?weekNumber=1&year=2025 - Stundenplan für eine Woche abrufen + @Get() + async getTimetable( + @Query('weekNumber') weekNumber?: string, + @Query('year') year?: string, + ) { + const week = weekNumber ? parseInt(weekNumber, 10) : undefined; + const yr = year ? parseInt(year, 10) : undefined; + return this.timetableService.getTimetable(week, yr); + } + + // POST /timetable - Neuen Stundenplan-Eintrag erstellen (nur Lehrer) + @Post() + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + async createEntry(@Body() createDto: CreateTimetableEntryDto) { + return this.timetableService.createEntry(createDto); + } + + // PUT /timetable/:id - Stundenplan-Eintrag aktualisieren (nur Lehrer) + @Put(':id') + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + async updateEntry( + @Param('id', ParseIntPipe) entryId: number, + @Body() updateDto: UpdateTimetableEntryDto, + ) { + return this.timetableService.updateEntry(entryId, updateDto); + } + + // DELETE /timetable/:id - Stundenplan-Eintrag löschen (nur Lehrer) + @Delete(':id') + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + async deleteEntry(@Param('id', ParseIntPipe) entryId: number) { + return this.timetableService.deleteEntry(entryId); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/timetable/timetable.module.ts b/Smartes-Klassenzimmer-Backend/src/timetable/timetable.module.ts new file mode 100644 index 0000000..b8d0c05 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/timetable/timetable.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TimetableController } from './timetable.controller'; +import { TimetableService } from './timetable.service'; +import { PrismaModule } from '../prisma/prisma.module'; + +@Module({ + imports: [PrismaModule], + controllers: [TimetableController], + providers: [TimetableService], +}) +export class TimetableModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/timetable/timetable.service.ts b/Smartes-Klassenzimmer-Backend/src/timetable/timetable.service.ts new file mode 100644 index 0000000..f2818be --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/timetable/timetable.service.ts @@ -0,0 +1,180 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { CreateTimetableEntryDto, UpdateTimetableEntryDto } from './dto'; + +@Injectable() +export class TimetableService { + constructor(private prisma: PrismaService) {} + + // Alle Stundenplan-Einträge für eine bestimmte Woche abrufen + async getTimetable(weekNumber?: number, year?: number) { + const currentDate = new Date(); + const targetWeek = weekNumber || this.getWeekNumber(currentDate); + const targetYear = year || currentDate.getFullYear(); + + return this.prisma.timetableEntry.findMany({ + where: { + OR: [ + // Wiederkehrende Einträge (für alle Wochen) + { isRecurring: true }, + // Spezifische Einträge für diese Woche + { + AND: [ + { weekNumber: targetWeek }, + { year: targetYear }, + { isRecurring: false }, + ], + }, + ], + }, + include: { + subject: true, // Subject-Details laden + teacher: { // Lehrer-Details laden + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, // Raum-Details laden + }, + orderBy: [{ dayOfWeek: 'asc' }, { startTime: 'asc' }], + }); + } + + // Kalenderwoche berechnen (ISO 8601) + private getWeekNumber(date: Date): number { + const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); + const dayNum = d.getUTCDay() || 7; + d.setUTCDate(d.getUTCDate() + 4 - dayNum); + const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); + return Math.ceil((((d.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); + } + + // Einen neuen Stundenplan-Eintrag erstellen (nur für Lehrer) + async createEntry(dto: CreateTimetableEntryDto) { + let subjectId = dto.subjectId; + if (!subjectId && dto.subjectName) { + // Find or create subject + const subject = await this.prisma.subject.upsert({ + where: { name: dto.subjectName }, + update: {}, + create: { name: dto.subjectName }, + }); + subjectId = subject.id; + } + + if (!subjectId) { + throw new Error('Subject ID or Name must be provided'); + } + + let roomId = dto.roomId; + if (!roomId && dto.roomNumber) { + // Find or create room + const room = await this.prisma.room.upsert({ + where: { number: dto.roomNumber }, + update: {}, + create: { number: dto.roomNumber }, + }); + roomId = room.id; + } + + return this.prisma.timetableEntry.create({ + data: { + dayOfWeek: dto.dayOfWeek, + startTime: dto.startTime, + endTime: dto.endTime, + subjectId: subjectId, + teacherId: dto.teacherId, + roomId: roomId, + weekNumber: dto.weekNumber, + year: dto.year, + isRecurring: dto.isRecurring ?? true, // Standard: wiederkehrend + allowStudentUploads: dto.allowStudentUploads ?? false, + }, + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + }, + }); + } + + // Einen Stundenplan-Eintrag aktualisieren (nur für Lehrer) + async updateEntry(entryId: number, dto: UpdateTimetableEntryDto) { + // Prüfen, ob der Eintrag existiert + const entry = await this.prisma.timetableEntry.findUnique({ + where: { id: entryId }, + }); + + if (!entry) { + throw new NotFoundException('Stundenplan-Eintrag nicht gefunden'); + } + + let subjectId = dto.subjectId; + if (!subjectId && dto.subjectName) { + const subject = await this.prisma.subject.upsert({ + where: { name: dto.subjectName }, + update: {}, + create: { name: dto.subjectName }, + }); + subjectId = subject.id; + } + + let roomId = dto.roomId; + if (!roomId && dto.roomNumber) { + const room = await this.prisma.room.upsert({ + where: { number: dto.roomNumber }, + update: {}, + create: { number: dto.roomNumber }, + }); + roomId = room.id; + } + + const data: any = { ...dto }; + if (subjectId) data.subjectId = subjectId; + if (roomId) data.roomId = roomId; + delete data.subjectName; + delete data.roomNumber; + + return this.prisma.timetableEntry.update({ + where: { id: entryId }, + data: data, + include: { + subject: true, + teacher: { + select: { + id: true, + username: true, + email: true, + }, + }, + room: true, + }, + }); + } + + // Einen Stundenplan-Eintrag löschen (nur für Lehrer) + async deleteEntry(entryId: number) { + // Prüfen, ob der Eintrag existiert + const entry = await this.prisma.timetableEntry.findUnique({ + where: { id: entryId }, + }); + + if (!entry) { + throw new NotFoundException('Stundenplan-Eintrag nicht gefunden'); + } + + await this.prisma.timetableEntry.delete({ + where: { id: entryId }, + }); + + return { message: 'Stundenplan-Eintrag erfolgreich gelöscht' }; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/users/users.controller.ts b/Smartes-Klassenzimmer-Backend/src/users/users.controller.ts new file mode 100644 index 0000000..d22680a --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/users/users.controller.ts @@ -0,0 +1,52 @@ +import { + Controller, + Get, + Patch, + Delete, + Param, + Body, + UseGuards, +} from '@nestjs/common'; +import { UsersService } from './users.service'; +import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; +import { RolesGuard, Roles } from '../auth/guards/roles.guard'; + +@Controller('users') +@UseGuards(JwtAuthGuard) +export class UsersController { + constructor(private readonly usersService: UsersService) {} + + @Get('teachers') + async findTeachers() { + const users = await this.usersService.findAll(); + return users.filter(user => user.role === 'Lehrer'); + } + + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + @Get() + findAll() { + return this.usersService.findAll(); + } + + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + @Get(':id') + findOne(@Param('id') id: string) { + return this.usersService.findOne(+id); + } + + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + @Patch(':id') + update(@Param('id') id: string, @Body() updateData: any) { + return this.usersService.update(+id, updateData); + } + + @UseGuards(RolesGuard) + @Roles(['Lehrer']) + @Delete(':id') + remove(@Param('id') id: string) { + return this.usersService.remove(+id); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/users/users.module.ts b/Smartes-Klassenzimmer-Backend/src/users/users.module.ts new file mode 100644 index 0000000..c8dc41a --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/users/users.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { UsersController } from './users.controller'; +import { UsersService } from './users.service'; +import { PrismaModule } from '../prisma/prisma.module'; + +@Module({ + imports: [PrismaModule], + controllers: [UsersController], + providers: [UsersService], +}) +export class UsersModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/users/users.service.ts b/Smartes-Klassenzimmer-Backend/src/users/users.service.ts new file mode 100644 index 0000000..af2045c --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/users/users.service.ts @@ -0,0 +1,66 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import * as bcrypt from 'bcrypt'; + +@Injectable() +export class UsersService { + constructor(private prisma: PrismaService) {} + + async findAll() { + return await this.prisma.user.findMany({ + select: { + id: true, + email: true, + username: true, + role: true, + createdAt: true, + updatedAt: true, + }, + }); + } + + async findOne(id: number) { + return await this.prisma.user.findUnique({ + where: { id }, + select: { + id: true, + email: true, + username: true, + role: true, + createdAt: true, + updatedAt: true, + }, + }); + } + + async update(id: number, updateData: any) { + const data: any = { + email: updateData.email, + username: updateData.username, + role: updateData.role, + }; + + if (updateData.password) { + data.password = await bcrypt.hash(updateData.password, 10); + } + + return await this.prisma.user.update({ + where: { id }, + data, + select: { + id: true, + email: true, + username: true, + role: true, + createdAt: true, + updatedAt: true, + }, + }); + } + + async remove(id: number) { + return await this.prisma.user.delete({ + where: { id }, + }); + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/whiteboard/dto/whiteboard-draw.dto.ts b/Smartes-Klassenzimmer-Backend/src/whiteboard/dto/whiteboard-draw.dto.ts new file mode 100644 index 0000000..0644013 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/whiteboard/dto/whiteboard-draw.dto.ts @@ -0,0 +1,77 @@ +import { IsString, IsNumber, IsArray, IsOptional, ValidateNested, IsEnum } from 'class-validator'; +import { Type } from 'class-transformer'; + +export enum DrawingTool { + PEN = 'pen', + ERASER = 'eraser', + LINE = 'line', + RECTANGLE = 'rectangle', + CIRCLE = 'circle', + TEXT = 'text', +} + +export class Point { + @IsNumber() + x: number; + + @IsNumber() + y: number; +} + +export class WhiteboardDrawDto { + @IsString() + whiteboardId: string; + + @IsEnum(DrawingTool) + tool: DrawingTool; + + @IsArray() + @ValidateNested({ each: true }) + @Type(() => Point) + points: Point[]; + + @IsString() + @IsOptional() + color?: string; + + @IsNumber() + @IsOptional() + lineWidth?: number; + + @IsString() + @IsOptional() + text?: string; + + @IsString() + userId: string; +} + +export class WhiteboardClearDto { + @IsString() + whiteboardId: string; + + @IsString() + userId: string; +} + +export class WhiteboardJoinDto { + @IsString() + whiteboardId: string; + + @IsString() + userId: string; +} + +export class WhiteboardCursorDto { + @IsString() + whiteboardId: string; + + @IsString() + userId: string; + + @IsNumber() + x: number; + + @IsNumber() + y: number; +} diff --git a/Smartes-Klassenzimmer-Backend/src/whiteboard/whiteboard.gateway.ts b/Smartes-Klassenzimmer-Backend/src/whiteboard/whiteboard.gateway.ts new file mode 100644 index 0000000..d7fce86 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/whiteboard/whiteboard.gateway.ts @@ -0,0 +1,234 @@ +import { + WebSocketGateway, + SubscribeMessage, + MessageBody, + WebSocketServer, + ConnectedSocket, + OnGatewayConnection, + OnGatewayDisconnect, +} from '@nestjs/websockets'; +import { UseGuards } from '@nestjs/common'; +import { Server, Socket } from 'socket.io'; +import { JwtService } from '@nestjs/jwt'; +import { WsJwtGuard } from '../auth/guards'; +import { WhiteboardService } from './whiteboard.service'; +import { + WhiteboardDrawDto, + WhiteboardClearDto, + WhiteboardJoinDto, + WhiteboardCursorDto, +} from './dto/whiteboard-draw.dto'; +import { Logger } from '@nestjs/common'; + +@WebSocketGateway({ + cors: { + origin: [ + 'http://localhost:5173', + 'http://127.0.0.1:5173', + 'http://localhost:5500', + 'http://127.0.0.1:5500', + 'http://localhost:3000', + ], + credentials: true, + }, + namespace: '/whiteboard', +}) +export class WhiteboardGateway implements OnGatewayConnection, OnGatewayDisconnect { + @WebSocketServer() + server: Server; + + private readonly logger = new Logger(WhiteboardGateway.name); + private userSockets: Map = new Map(); + + constructor( + private readonly jwtService: JwtService, + private readonly whiteboardService: WhiteboardService, + ) {} + + handleConnection(client: Socket) { + try { + const token = this.extractTokenFromHandshake(client); + + if (!token) { + this.logger.log(`Client ${client.id} rejected: No token provided`); + client.emit('error', { message: 'Unauthorized: No token provided' }); + client.disconnect(); + return; + } + + const payload = this.jwtService.verify(token, { + secret: process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-this-in-production', + }); + + // User-Daten zum Socket hinzufügen + client.data.user = payload; + + this.logger.log(`Client connected: ${client.id}, User: ${payload.username}`); + } catch (error) { + this.logger.log(`Client ${client.id} rejected: Invalid token`); + client.emit('error', { message: 'Unauthorized: Invalid token' }); + client.disconnect(); + } + } + + private extractTokenFromHandshake(client: Socket): string | null { + // Token aus Cookie extrahieren + const cookies = client.handshake.headers.cookie; + if (cookies) { + const cookieArray = cookies.split(';'); + for (const cookie of cookieArray) { + const [name, value] = cookie.trim().split('='); + if (name === 'access_token') { + return value; + } + } + } + + // Fallback: Token aus auth object + const token = client.handshake.auth?.token; + if (token) { + return token; + } + + // Fallback: Token aus query parameter + const queryToken = client.handshake.query?.token as string; + if (queryToken) { + return queryToken; + } + + return null; + } + + handleDisconnect(client: Socket) { + this.logger.log(`Client disconnected: ${client.id}`); + const userData = this.userSockets.get(client.id); + + if (userData) { + this.whiteboardService.leaveWhiteboard(userData.whiteboardId, userData.userId); + + // Benachrichtige andere Benutzer + this.server.to(userData.whiteboardId).emit('user-left', { + userId: userData.userId, + activeUsers: this.whiteboardService.getActiveUsers(userData.whiteboardId), + }); + + this.userSockets.delete(client.id); + } + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('join-whiteboard') + handleJoinWhiteboard( + @MessageBody() data: WhiteboardJoinDto, + @ConnectedSocket() client: Socket, + ) { + const { whiteboardId, userId } = data; + + this.logger.log(`User ${userId} joining whiteboard ${whiteboardId}`); + + // Verlasse altes Whiteboard falls vorhanden + const oldData = this.userSockets.get(client.id); + if (oldData) { + client.leave(oldData.whiteboardId); + this.whiteboardService.leaveWhiteboard(oldData.whiteboardId, oldData.userId); + } + + // Trete neuem Whiteboard bei + client.join(whiteboardId); + this.whiteboardService.joinWhiteboard(whiteboardId, userId); + this.userSockets.set(client.id, { whiteboardId, userId }); + + // Sende aktuelle Whiteboard-Daten an den neuen Benutzer + const currentData = this.whiteboardService.getWhiteboardData(whiteboardId); + client.emit('whiteboard-state', currentData); + + // Benachrichtige andere Benutzer über den neuen Teilnehmer + const activeUsers = this.whiteboardService.getActiveUsers(whiteboardId); + this.server.to(whiteboardId).emit('user-joined', { + userId, + activeUsers, + }); + + return { + success: true, + activeUsers, + }; + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('draw') + handleDraw( + @MessageBody() data: WhiteboardDrawDto, + @ConnectedSocket() client: Socket, + ) { + const { whiteboardId } = data; + + // Speichere die Zeichnung + const drawingData = this.whiteboardService.addDrawing(whiteboardId, data); + + // Sende die Zeichnung an alle anderen Benutzer im Whiteboard (außer dem Sender) + client.to(whiteboardId).emit('drawing', drawingData); + + return { success: true }; + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('clear-whiteboard') + handleClearWhiteboard( + @MessageBody() data: WhiteboardClearDto, + @ConnectedSocket() client: Socket, + ) { + const { whiteboardId, userId } = data; + + this.logger.log(`User ${userId} clearing whiteboard ${whiteboardId}`); + + // Lösche das Whiteboard + this.whiteboardService.clearWhiteboard(whiteboardId); + + // Benachrichtige alle Benutzer + this.server.to(whiteboardId).emit('whiteboard-cleared', { userId }); + + return { success: true }; + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('cursor-move') + handleCursorMove( + @MessageBody() data: WhiteboardCursorDto, + @ConnectedSocket() client: Socket, + ) { + const { whiteboardId, userId, x, y } = data; + + // Sende Cursor-Position an alle anderen Benutzer + client.to(whiteboardId).emit('cursor-position', { + userId, + x, + y, + }); + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('undo') + handleUndo( + @MessageBody() data: { whiteboardId: string; userId: string }, + @ConnectedSocket() client: Socket, + ) { + const { whiteboardId, userId } = data; + + this.logger.log(`User ${userId} requesting undo on whiteboard ${whiteboardId}`); + + // Benachrichtige alle Benutzer über Undo + this.server.to(whiteboardId).emit('undo-requested', { userId }); + + return { success: true }; + } + + @UseGuards(WsJwtGuard) + @SubscribeMessage('get-active-users') + handleGetActiveUsers( + @MessageBody() data: { whiteboardId: string }, + ) { + const activeUsers = this.whiteboardService.getActiveUsers(data.whiteboardId); + return { activeUsers }; + } +} diff --git a/Smartes-Klassenzimmer-Backend/src/whiteboard/whiteboard.module.ts b/Smartes-Klassenzimmer-Backend/src/whiteboard/whiteboard.module.ts new file mode 100644 index 0000000..522f920 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/whiteboard/whiteboard.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { JwtModule } from '@nestjs/jwt'; +import { WhiteboardGateway } from './whiteboard.gateway'; +import { WhiteboardService } from './whiteboard.service'; + +@Module({ + imports: [JwtModule], + providers: [WhiteboardGateway, WhiteboardService], + exports: [WhiteboardService], +}) +export class WhiteboardModule {} diff --git a/Smartes-Klassenzimmer-Backend/src/whiteboard/whiteboard.service.ts b/Smartes-Klassenzimmer-Backend/src/whiteboard/whiteboard.service.ts new file mode 100644 index 0000000..0607bcc --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/src/whiteboard/whiteboard.service.ts @@ -0,0 +1,62 @@ +import { Injectable } from '@nestjs/common'; +import { WhiteboardDrawDto } from './dto/whiteboard-draw.dto'; + +export interface DrawingData extends WhiteboardDrawDto { + timestamp: number; + id: string; +} + +@Injectable() +export class WhiteboardService { + // In-Memory Storage für Whiteboard-Daten (später kann man das in eine DB auslagern) + private whiteboards: Map = new Map(); + private activeUsers: Map> = new Map(); + + addDrawing(whiteboardId: string, drawing: WhiteboardDrawDto): DrawingData { + const drawingData: DrawingData = { + ...drawing, + timestamp: Date.now(), + id: `${Date.now()}-${Math.random()}`, + }; + + if (!this.whiteboards.has(whiteboardId)) { + this.whiteboards.set(whiteboardId, []); + } + + this.whiteboards.get(whiteboardId)!.push(drawingData); + return drawingData; + } + + clearWhiteboard(whiteboardId: string): void { + this.whiteboards.set(whiteboardId, []); + } + + getWhiteboardData(whiteboardId: string): DrawingData[] { + return this.whiteboards.get(whiteboardId) || []; + } + + joinWhiteboard(whiteboardId: string, userId: string): void { + if (!this.activeUsers.has(whiteboardId)) { + this.activeUsers.set(whiteboardId, new Set()); + } + this.activeUsers.get(whiteboardId)!.add(userId); + } + + leaveWhiteboard(whiteboardId: string, userId: string): void { + const users = this.activeUsers.get(whiteboardId); + if (users) { + users.delete(userId); + if (users.size === 0) { + this.activeUsers.delete(whiteboardId); + } + } + } + + getActiveUsers(whiteboardId: string): string[] { + return Array.from(this.activeUsers.get(whiteboardId) || []); + } + + getActiveUserCount(whiteboardId: string): number { + return this.activeUsers.get(whiteboardId)?.size || 0; + } +} diff --git a/Smartes-Klassenzimmer-Backend/tsconfig.build.json b/Smartes-Klassenzimmer-Backend/tsconfig.build.json new file mode 100644 index 0000000..64f86c6 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/Smartes-Klassenzimmer-Backend/tsconfig.json b/Smartes-Klassenzimmer-Backend/tsconfig.json new file mode 100644 index 0000000..aba29b0 --- /dev/null +++ b/Smartes-Klassenzimmer-Backend/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "module": "nodenext", + "moduleResolution": "nodenext", + "resolvePackageJsonExports": true, + "esModuleInterop": true, + "isolatedModules": true, + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2023", + "sourceMap": true, + "outDir": "./dist", + "baseUrl": "./", + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": true, + "forceConsistentCasingInFileNames": true, + "noImplicitAny": false, + "strictBindCallApply": false, + "noFallthroughCasesInSwitch": false + } +} diff --git a/Smartes-Klassenzimmer-Backend/uploads/1765295849966-65353750.png b/Smartes-Klassenzimmer-Backend/uploads/1765295849966-65353750.png new file mode 100644 index 0000000..cd8daf2 Binary files /dev/null and b/Smartes-Klassenzimmer-Backend/uploads/1765295849966-65353750.png differ diff --git a/Smartes-Klassenzimmer-Frontend/.gitignore b/Smartes-Klassenzimmer-Frontend/.gitignore new file mode 100644 index 0000000..c6390ec --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/.gitignore @@ -0,0 +1,27 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +.vscode +!.vscode/extensions.json +!.vscode/mcp.json diff --git a/Smartes-Klassenzimmer-Frontend/.npmrc b/Smartes-Klassenzimmer-Frontend/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/Smartes-Klassenzimmer-Frontend/Dockerfile b/Smartes-Klassenzimmer-Frontend/Dockerfile new file mode 100644 index 0000000..f38617d --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/Dockerfile @@ -0,0 +1,14 @@ +FROM node:22-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . + +RUN npm run build + +EXPOSE 3000 + +CMD ["node", "build/index.js"] diff --git a/Smartes-Klassenzimmer-Frontend/README.md b/Smartes-Klassenzimmer-Frontend/README.md new file mode 100644 index 0000000..42ed03d --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/README.md @@ -0,0 +1,25 @@ +# Smartes Klassenzimmer - Frontend + +Vollständige Projekt-Dokumentation befindet sich in der [CLAUDE.md](../CLAUDE.md) im Root-Verzeichnis. + +## Quick Start + +```bash +# Dependencies installieren +npm install + +# Development Server starten +npm run dev +``` + +Der Server läuft auf `http://localhost:5173`. + +## Building + +```bash +# Production Build erstellen +npm run build + +# Production Build testen +npm run preview +``` diff --git a/Smartes-Klassenzimmer-Frontend/package-lock.json b/Smartes-Klassenzimmer-Frontend/package-lock.json new file mode 100644 index 0000000..eb57ba8 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/package-lock.json @@ -0,0 +1,1926 @@ +{ + "name": "smartes-klassenzimmer-frontend", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "smartes-klassenzimmer-frontend", + "version": "0.0.1", + "dependencies": { + "lucide-svelte": "^0.556.0", + "socket.io-client": "^4.8.1" + }, + "devDependencies": { + "@sveltejs/adapter-node": "^5.4.0", + "@sveltejs/kit": "^2.47.1", + "@sveltejs/vite-plugin-svelte": "^6.2.1", + "svelte": "^5.41.0", + "svelte-check": "^4.3.3", + "typescript": "^5.9.3", + "vite": "^7.1.10" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "28.0.9", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.9.tgz", + "integrity": "sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", + "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", + "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", + "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", + "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", + "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", + "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", + "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", + "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", + "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", + "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", + "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", + "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", + "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", + "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", + "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", + "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", + "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", + "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", + "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", + "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", + "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.6.tgz", + "integrity": "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-node": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.4.0.tgz", + "integrity": "sha512-NMsrwGVPEn+J73zH83Uhss/hYYZN6zT3u31R3IHAn3MiKC3h8fjmIAhLfTSOeNHr5wPYfjjMg8E+1gyFgyrEcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-commonjs": "^28.0.1", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.0", + "rollup": "^4.9.5" + }, + "peerDependencies": { + "@sveltejs/kit": "^2.4.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.48.4", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.48.4.tgz", + "integrity": "sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", + "cookie": "^0.6.0", + "devalue": "^5.3.2", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "sade": "^1.8.1", + "set-cookie-parser": "^2.6.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", + "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", + "debug": "^4.4.1", + "deepmerge": "^4.3.1", + "magic-string": "^0.30.17", + "vitefu": "^1.1.1" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite": "^6.3.0 || ^7.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.1.tgz", + "integrity": "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.1" + }, + "engines": { + "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", + "svelte": "^5.0.0", + "vite": "^6.3.0 || ^7.0.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "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==", + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/devalue": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.4.2.tgz", + "integrity": "sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw==", + "dev": true, + "license": "MIT" + }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.2.tgz", + "integrity": "sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "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==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "license": "MIT" + }, + "node_modules/lucide-svelte": { + "version": "0.556.0", + "resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.556.0.tgz", + "integrity": "sha512-OUj2xk403WslZAIGnEF5wvw/A7QAUXebDWNFajU/INY9XPWRZZZdcr+h6zB6XAzd84F7lPZqiEi42JVjXtcBYw==", + "license": "ISC", + "peerDependencies": { + "svelte": "^3 || ^4 || ^5.0.0-next.42" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "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/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "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/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "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.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/rollup": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", + "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", + "dev": true, + "license": "MIT", + "peer": true, + "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.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", + "fsevents": "~2.3.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svelte": { + "version": "5.43.3", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.3.tgz", + "integrity": "sha512-kjkAjCk41mJfvJZG56XcJNOdJSke94JxtcX8zFzzz2vrt47E0LnoBzU6azIZ1aBxJgUep8qegAkguSf1GjxLXQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "esm-env": "^1.2.1", + "esrap": "^2.1.0", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-check": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.3.tgz", + "integrity": "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/svelte/node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.1.tgz", + "integrity": "sha512-qTl3VF7BvOupTR85Zc561sPEgxyUSNSvTQ9fit7DEMP7yPgvvIGm5Zfa1dOM+kOwWGNviK9uFM9ra77+OjK7lQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", + "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "license": "MIT" + } + } +} diff --git a/Smartes-Klassenzimmer-Frontend/package.json b/Smartes-Klassenzimmer-Frontend/package.json new file mode 100644 index 0000000..5016e61 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/package.json @@ -0,0 +1,27 @@ +{ + "name": "smartes-klassenzimmer-frontend", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "devDependencies": { + "@sveltejs/adapter-node": "^5.4.0", + "@sveltejs/kit": "^2.47.1", + "@sveltejs/vite-plugin-svelte": "^6.2.1", + "svelte": "^5.41.0", + "svelte-check": "^4.3.3", + "typescript": "^5.9.3", + "vite": "^7.1.10" + }, + "dependencies": { + "lucide-svelte": "^0.556.0", + "socket.io-client": "^4.8.1" + } +} diff --git a/Smartes-Klassenzimmer-Frontend/src/app.css b/Smartes-Klassenzimmer-Frontend/src/app.css new file mode 100644 index 0000000..e9ebad3 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/app.css @@ -0,0 +1,891 @@ +/* ============================================ + SMARTROOM DESIGN SYSTEM + Minimalistisch · Klar · Professionell + ============================================ */ + +/* ============================================ + 1. DESIGN TOKENS + ============================================ */ + +:root { + /* === Farben === */ + + /* Neutral (Graustufen) - Minimalistisches Design */ + --neutral-50: #fafafa; + --neutral-100: #f5f5f5; + --neutral-200: #e0e0e0; + --neutral-300: #ddd; + --neutral-400: #999; + --neutral-500: #666; + --neutral-600: #333; + --neutral-700: #333; + --neutral-800: #222; + --neutral-900: #111; + --neutral-950: #000; + + /* Primary (Blau als Hauptfarbe aus DESIGN_SYSTEM.md) */ + --primary-50: #eef0fe; + --primary-100: #dde1fd; + --primary-200: #bcc4fb; + --primary-300: #9aa6f9; + --primary-400: #7989f7; + --primary-500: #3C45F0; + --primary-600: #2d34d1; + --primary-700: #2328a8; + --primary-800: #1a1d7f; + --primary-900: #101256; + + /* Shortcuts für einfache Verwendung */ + --primary: var(--primary-500); + --primary-hover: var(--primary-600); + --primary-light: var(--primary-400); + + /* Semantic Colors */ + --success-50: #ecfdf5; + --success-200: #a7f3d0; + --success-400: #34d399; + --success-500: #10b981; + --success-700: #047857; + + --error-50: #fef2f2; + --error-200: #fecaca; + --error-400: #f87171; + --error-500: #ef4444; + --error-700: #b91c1c; + + --warning-50: #fffbeb; + --warning-200: #fde68a; + --warning-400: #fbbf24; + --warning-500: #f59e0b; + --warning-700: #b45309; + + --info-50: #eff6ff; + --info-500: #3b82f6; + --info-700: #1d4ed8; + + /* === Spacing === */ + --space-xs: 0.25rem; /* 4px */ + --space-sm: 0.5rem; /* 8px */ + --space-md: 1rem; /* 16px */ + --space-lg: 1.5rem; /* 24px */ + --space-xl: 2rem; /* 32px */ + --space-2xl: 3rem; /* 48px */ + --space-3xl: 4rem; /* 64px */ + + /* === Typography === */ + --font-heading: 'Noto Serif', Georgia, serif; + --font-body: 'Figtree', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + + --text-xs: 0.75rem; /* 12px */ + --text-sm: 0.875rem; /* 14px */ + --text-base: 1rem; /* 16px */ + --text-lg: 1.125rem; /* 18px */ + --text-xl: 1.25rem; /* 20px */ + --text-2xl: 1.5rem; /* 24px */ + --text-3xl: 1.875rem; /* 30px */ + --text-4xl: 2.25rem; /* 36px */ + --text-5xl: 3rem; /* 48px */ + + --font-light: 300; + --font-normal: 400; + --font-medium: 500; + --font-semibold: 600; + --font-bold: 700; + + /* === Border Radius === */ + --radius-sm: 0.375rem; /* 6px */ + --radius-md: 0.5rem; /* 8px */ + --radius-lg: 0.5rem; /* 8px */ + --radius-xl: 0.5rem; /* 8px */ + --radius-2xl: 0.5rem; /* 8px */ + --radius-full: 9999px; + + /* === Shadows - Minimiert === */ + --shadow-sm: none; + --shadow-md: none; + --shadow-lg: none; + --shadow-xl: none; + + /* === Transitions === */ + --transition-fast: 150ms ease; + --transition-base: 200ms ease; + --transition-slow: 300ms ease; + + /* === Z-Index === */ + --z-dropdown: 1000; + --z-sticky: 1020; + --z-fixed: 1030; + --z-modal-backdrop: 1040; + --z-modal: 1050; + --z-popover: 1060; + --z-tooltip: 1070; +} + +/* ============================================ + 2. RESET & BASE STYLES + ============================================ */ + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font-family: var(--font-body); + font-size: var(--text-base); + line-height: 1.6; + color: var(--neutral-900); + background: #fafafa; +} + +/* === Typography === */ +h1 { + font-family: var(--font-heading); /* Noto Serif nur für große Hauptüberschriften */ + font-weight: var(--font-semibold); + line-height: 1.3; + color: var(--neutral-900); + font-size: var(--text-4xl); +} + +h2, h3, h4, h5, h6 { + font-family: var(--font-body); /* Figtree für alle anderen Überschriften */ + font-weight: var(--font-semibold); + line-height: 1.3; + color: var(--neutral-900); +} + +h2 { font-size: var(--text-3xl); } +h3 { font-size: var(--text-2xl); } +h4 { font-size: var(--text-xl); } +h5 { font-size: var(--text-lg); } +h6 { font-size: var(--text-base); } + +p { + margin-bottom: var(--space-md); + color: var(--neutral-700); +} + +a { + color: var(--primary); + text-decoration: none; + transition: color var(--transition-fast); +} + +a:hover { + color: var(--primary-light); +} + +/* === Lists === */ +ul, ol { + padding-left: var(--space-lg); +} + +li { + margin-bottom: var(--space-sm); +} + +/* ============================================ + 3. LAYOUT + ============================================ */ + +.container { + width: 100%; + max-width: 900px; + margin: 0 auto; + padding: 0 var(--space-md); +} + +.container-fluid { + width: 100%; + max-width: 100%; + padding: 0 var(--space-md); +} + +.container-wide { + max-width: 1200px; +} + +.container-narrow { + max-width: 600px; +} + +/* === Grid === */ +.grid { + display: grid; + gap: var(--space-lg); +} + +.grid-cols-2 { + grid-template-columns: repeat(2, 1fr); +} + +.grid-cols-3 { + grid-template-columns: repeat(3, 1fr); +} + +.grid-cols-4 { + grid-template-columns: repeat(4, 1fr); +} + +@media (max-width: 768px) { + .grid-cols-2, + .grid-cols-3, + .grid-cols-4 { + grid-template-columns: 1fr; + } +} + +/* === Flexbox Utilities === */ +.flex { + display: flex; +} + +.flex-col { + flex-direction: column; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.gap-sm { gap: var(--space-sm); } +.gap-md { gap: var(--space-md); } +.gap-lg { gap: var(--space-lg); } + +/* ============================================ + 4. KOMPONENTEN + ============================================ */ + +/* === Card === */ +.card { + background: white; + border-radius: 8px; + padding: 1.25rem; + border: 1px solid var(--neutral-200); +} + +.card-compact { + padding: var(--space-md); +} + +.card-title { + font-family: var(--font-body); + font-size: var(--text-lg); + font-weight: var(--font-semibold); + margin-bottom: var(--space-sm); + color: var(--neutral-900); +} + +.card-subtitle { + font-size: var(--text-sm); + color: var(--neutral-600); + margin-bottom: var(--space-sm); +} + +.card-text { + font-size: var(--text-base); + color: var(--neutral-700); +} + +/* === Button === */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--space-sm); + padding: 0.625rem 1rem; + font-family: var(--font-body); + font-size: var(--text-sm); + font-weight: 500; + line-height: 1; + border: 1px solid var(--neutral-300); + border-radius: 6px; + cursor: pointer; + transition: all 0.15s; + text-decoration: none; + background: white; + color: var(--neutral-600); +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn:hover:not(:disabled) { + border-color: var(--neutral-900); + background: var(--neutral-100); +} + +/* Button Variants */ +.btn-primary { + background: var(--primary-500); + color: white; + border-color: var(--primary-500); +} + +.btn-primary:hover:not(:disabled) { + background: var(--primary-600); + border-color: var(--primary-600); +} + +.btn-secondary { + background: var(--neutral-200); + color: var(--neutral-700); + border-color: var(--neutral-300); +} + +.btn-secondary:hover:not(:disabled) { + background: var(--neutral-300); + border-color: var(--neutral-400); +} + +.btn-ghost { + background: transparent; + color: var(--neutral-900); + border-color: transparent; +} + +.btn-ghost:hover:not(:disabled) { + background: var(--neutral-100); +} + +.btn-success { + background: var(--success-500); + color: white; + border-color: var(--success-500); +} + +.btn-success:hover:not(:disabled) { + background: var(--success-700); +} +/* Button Sizes */ +.btn-sm, .btn-small { + padding: 0.375rem 0.75rem; + font-size: 0.75rem; +} + +.btn-lg { + padding: 0.875rem 1.5rem; + font-size: var(--text-base); +} + +.btn-block { + width: 100%; +} + +/* === Form Elements === */ +.form-group { + display: flex; + flex-direction: column; + gap: var(--space-sm); + margin-bottom: var(--space-md); +} + +.form-label { + font-size: var(--text-sm); + font-weight: var(--font-medium); + color: var(--neutral-700); +} + +.form-input, +.form-select, +.form-textarea, +input:not([type="checkbox"]):not([type="radio"]), +select, +textarea { + width: 100%; + padding: 0.625rem 0.875rem; + font-family: var(--font-body); + font-size: var(--text-sm); + line-height: 1.5; + color: var(--neutral-900); + background: white; + border: 1px solid var(--neutral-300); + border-radius: 6px; + transition: all var(--transition-fast); +} + +.form-input:focus, +.form-select:focus, +.form-textarea:focus, +input:focus, +select:focus, +textarea:focus { + outline: none; + border-color: var(--primary-500); +} + +.form-input.error, +.form-select.error, +.form-textarea.error { + border-color: var(--error-500); +} + +.form-error { + font-size: var(--text-xs); + color: var(--error-700); +} + +.form-help { + font-size: var(--text-xs); + color: var(--neutral-600); +} + +/* === Badge === */ +.badge { + display: inline-block; + padding: 0.25rem 0.625rem; + font-size: 0.75rem; + font-weight: 600; + line-height: 1; + border-radius: 4px; + text-transform: uppercase; + letter-spacing: 0.025em; + border: 1px solid var(--neutral-200); +} + +.badge-default { + background: #f0f0f0; + color: var(--neutral-500); + border-color: var(--neutral-200); +} + +.badge-primary, .badge.admin { + background: var(--neutral-900); + color: white; + border-color: var(--neutral-900); +} + +.badge-success { + background: var(--success-50); + color: var(--success-700); + border: 1px solid var(--success-500); +} + +.badge-error { + background: var(--error-50); + color: var(--error-700); + border: 1px solid var(--error-500); +} + +.badge-warning { + background: var(--warning-50); + color: var(--warning-700); + border: 1px solid var(--warning-500); +} + +/* === Avatar === */ +.avatar { + display: inline-flex; + align-items: center; + justify-content: center; + width: 80px; + height: 80px; + font-size: 2rem; + font-weight: var(--font-bold); + color: white; + background: var(--primary); + border-radius: 50%; + margin: 0 auto; +} + +.avatar-sm { + width: 32px; + height: 32px; + font-size: 0.875rem; +} + +.avatar-md { + width: 48px; + height: 48px; + font-size: 1.25rem; +} + +.avatar-lg { + width: 80px; + height: 80px; + font-size: 2rem; +} + +/* === Info Grid === */ +.info-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: var(--space-md); +} + +.info-item { + padding: 0.75rem; + background: var(--neutral-50); + border: 1px solid var(--neutral-200); + border-radius: var(--radius-sm); +} + +.info-label { + font-size: var(--text-xs); + font-weight: var(--font-semibold); + color: var(--neutral-600); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: var(--space-xs); +} + +.info-value { + font-size: var(--text-sm); + font-weight: var(--font-semibold); + color: var(--neutral-900); +} + +@media (max-width: 768px) { + .info-grid { + grid-template-columns: 1fr; + } +} + +/* === Stats === */ +.stat-display { + display: flex; + align-items: baseline; + justify-content: center; + gap: var(--space-sm); +} + +.stat-number { + font-family: var(--font-heading); + font-size: var(--text-4xl); + font-weight: var(--font-bold); + color: var(--primary-500); + line-height: 1; +} + +.stat-label { + font-size: var(--text-sm); + color: var(--neutral-600); +} + +.stat-divider { + font-size: var(--text-2xl); + color: var(--neutral-400); +} + +.stat-total { + font-size: var(--text-2xl); + font-weight: var(--font-semibold); + color: var(--neutral-600); +} + +/* === Widget === */ +.widget { + padding: var(--space-md); + background: var(--neutral-50); + border: 1px solid var(--neutral-200); + border-radius: var(--radius-sm); +} + +.widget-title { + font-size: var(--text-xs); + font-weight: var(--font-semibold); + color: var(--neutral-600); + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: var(--space-md); +} + +.widget-content { + text-align: center; +} + +.widget-number { + font-size: var(--text-3xl); + font-weight: var(--font-bold); + color: var(--neutral-900); + line-height: 1; + margin-bottom: var(--space-xs); +} + +.widget-label { + font-size: var(--text-xs); + color: var(--neutral-600); +} + +/* === Table === */ +.table { + width: 100%; + border-collapse: collapse; +} + +.table th, +.table td { + padding: 0.75rem; + text-align: left; + border-bottom: 1px solid var(--neutral-200); +} + +.table th { + font-size: var(--text-xs); + font-weight: var(--font-semibold); + color: var(--neutral-600); + text-transform: uppercase; + letter-spacing: 0.05em; + background: var(--neutral-50); +} + +.table td { + font-size: var(--text-sm); + color: var(--neutral-900); +} + +.table tr:hover { + background: var(--neutral-50); +} + +/* === Alert === */ +.alert { + display: flex; + gap: var(--space-md); + padding: var(--space-md); + border: 1px solid; + border-radius: var(--radius-sm); + margin-bottom: var(--space-md); +} + +.alert-icon { + flex-shrink: 0; + font-size: var(--text-lg); +} + +.alert-content { + flex: 1; +} + +.alert-title { + font-size: var(--text-sm); + font-weight: var(--font-semibold); + margin-bottom: var(--space-xs); +} + +.alert-message { + font-size: var(--text-sm); +} + +.alert-success { + background: var(--success-50); + border-color: var(--success-500); + color: var(--success-700); +} + +.alert-error { + background: var(--error-50); + border-color: var(--error-500); + color: var(--error-700); +} + +.alert-warning { + background: var(--warning-50); + border-color: var(--warning-500); + color: var(--warning-700); +} + +.alert-info { + background: var(--info-50); + border-color: var(--info-500); + color: var(--info-700); +} + +/* === Loading === */ +.loading-spinner { + display: inline-block; + width: 1.5rem; + height: 1.5rem; + border: 2px solid var(--neutral-200); + border-top-color: var(--primary); + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +.loading-overlay { + position: fixed; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.9); + z-index: var(--z-modal); +} + +/* === Modal === */ +.modal-backdrop { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: var(--z-modal-backdrop); +} + +.modal { + background: white; + border-radius: var(--radius-md); + max-width: 600px; + width: 90%; + max-height: 90vh; + overflow-y: auto; + box-shadow: var(--shadow-xl); +} + +.modal-header { + padding: var(--space-lg); + border-bottom: 1px solid var(--neutral-200); +} + +.modal-title { + font-size: var(--text-xl); + font-weight: var(--font-semibold); + color: var(--neutral-900); +} + +.modal-body { + padding: var(--space-lg); +} + +.modal-footer { + padding: var(--space-lg); + border-top: 1px solid var(--neutral-200); + display: flex; + justify-content: flex-end; + gap: var(--space-md); +} + +/* === Empty State === */ +.empty-state { + text-align: center; + padding: var(--space-2xl) var(--space-lg); +} + +.empty-state-icon { + font-size: 3rem; + margin-bottom: var(--space-md); + opacity: 0.5; +} + +.empty-state-title { + font-size: var(--text-lg); + font-weight: var(--font-semibold); + color: var(--neutral-900); + margin-bottom: var(--space-sm); +} + +.empty-state-text { + font-size: var(--text-sm); + color: var(--neutral-600); +} + +/* ============================================ + 5. UTILITY CLASSES + ============================================ */ + +/* === Spacing === */ +.m-0 { margin: 0; } +.mt-0 { margin-top: 0; } +.mb-0 { margin-bottom: 0; } + +.m-sm { margin: var(--space-sm); } +.mt-sm { margin-top: var(--space-sm); } +.mb-sm { margin-bottom: var(--space-sm); } + +.m-md { margin: var(--space-md); } +.mt-md { margin-top: var(--space-md); } +.mb-md { margin-bottom: var(--space-md); } + +.m-lg { margin: var(--space-lg); } +.mt-lg { margin-top: var(--space-lg); } +.mb-lg { margin-bottom: var(--space-lg); } + +.p-0 { padding: 0; } +.pt-0 { padding-top: 0; } +.pb-0 { padding-bottom: 0; } + +.p-sm { padding: var(--space-sm); } +.pt-sm { padding-top: var(--space-sm); } +.pb-sm { padding-bottom: var(--space-sm); } + +.p-md { padding: var(--space-md); } +.pt-md { padding-top: var(--space-md); } +.pb-md { padding-bottom: var(--space-md); } + +.p-lg { padding: var(--space-lg); } +.pt-lg { padding-top: var(--space-lg); } +.pb-lg { padding-bottom: var(--space-lg); } + +/* === Text === */ +.text-xs { font-size: var(--text-xs); } +.text-sm { font-size: var(--text-sm); } +.text-base { font-size: var(--text-base); } +.text-lg { font-size: var(--text-lg); } +.text-xl { font-size: var(--text-xl); } + +.font-light { font-weight: var(--font-light); } +.font-normal { font-weight: var(--font-normal); } +.font-medium { font-weight: var(--font-medium); } +.font-semibold { font-weight: var(--font-semibold); } +.font-bold { font-weight: var(--font-bold); } + +.text-left { text-align: left; } +.text-center { text-align: center; } +.text-right { text-align: right; } + +.text-primary { color: var(--neutral-900); } +.text-secondary { color: var(--neutral-700); } +.text-muted { color: var(--neutral-600); } +.text-success { color: var(--success-700); } +.text-error { color: var(--error-700); } +.text-warning { color: var(--warning-700); } + +.uppercase { text-transform: uppercase; } + +/* === Display === */ +.block { display: block; } +.inline-block { display: inline-block; } +.hidden { display: none; } + +/* === Width === */ +.w-full { width: 100%; } +.w-auto { width: auto; } + +/* === Border === */ +.border { border: 1px solid var(--neutral-200); } +.border-t { border-top: 1px solid var(--neutral-200); } +.border-b { border-bottom: 1px solid var(--neutral-200); } + +/* === Border Radius === */ +.rounded-sm { border-radius: var(--radius-sm); } +.rounded-md { border-radius: var(--radius-md); } +.rounded-lg { border-radius: var(--radius-lg); } +.rounded-full { border-radius: var(--radius-full); } + +/* === Shadow === */ +.shadow-sm { box-shadow: var(--shadow-sm); } +.shadow-md { box-shadow: var(--shadow-md); } +.shadow-lg { box-shadow: var(--shadow-lg); } \ No newline at end of file diff --git a/Smartes-Klassenzimmer-Frontend/src/app.d.ts b/Smartes-Klassenzimmer-Frontend/src/app.d.ts new file mode 100644 index 0000000..da08e6d --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/Smartes-Klassenzimmer-Frontend/src/app.html b/Smartes-Klassenzimmer-Frontend/src/app.html new file mode 100644 index 0000000..f6909c1 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/app.html @@ -0,0 +1,14 @@ + + + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/Smartes-Klassenzimmer-Frontend/src/hooks.server.ts b/Smartes-Klassenzimmer-Frontend/src/hooks.server.ts new file mode 100644 index 0000000..d3c9c6b --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/hooks.server.ts @@ -0,0 +1,31 @@ +import { redirect } from '@sveltejs/kit'; +import type { Handle } from '@sveltejs/kit'; + +// Routen die NICHT authentifiziert sein müssen +const PUBLIC_ROUTES = ['/', '/login', '/register', '/forgot-password', '/reset-password']; + +// Routen die NUR für nicht-authentifizierte Nutzer sind (redirect zu Dashboard wenn eingeloggt) +const AUTH_ROUTES = ['/login', '/register', '/forgot-password', '/reset-password']; + +export const handle: Handle = async ({ event, resolve }) => { + const { url, cookies } = event; + const accessToken = cookies.get('access_token'); + + // 1. Wenn User eingeloggt ist und auf eine Auth-Seite will -> Redirect zum Dashboard + if (accessToken && AUTH_ROUTES.includes(url.pathname)) { + throw redirect(303, '/dashboard'); + } + + // 2. Wenn auf öffentlicher Route (und nicht oben abgefangen), einfach durchlassen + if (PUBLIC_ROUTES.includes(url.pathname)) { + return resolve(event); + } + + // 3. Wenn nicht authentifiziert und auf geschützter Route -> Redirect zum Login + if (!accessToken) { + throw redirect(303, '/login'); + } + + // Request normal weitergeben + return resolve(event); +}; diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/api.ts b/Smartes-Klassenzimmer-Frontend/src/lib/api.ts new file mode 100644 index 0000000..0971369 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/lib/api.ts @@ -0,0 +1,651 @@ +import { browser } from '$app/environment'; + +// API Client für Backend-Kommunikation +const API_BASE_URL = browser + ? (import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000/api') + : (import.meta.env.VITE_INTERNAL_API_URL || 'http://backend:3000/api'); + +interface ApiResponse { + data?: T; + error?: string; +} + +class ApiClient { + private baseUrl: string; + + constructor(baseUrl: string) { + this.baseUrl = baseUrl; + } + + private async request( + endpoint: string, + options: RequestInit = {} + ): Promise> { + try { + const headers: HeadersInit = { ...options.headers }; + + // Content-Type nur setzen, wenn kein FormData gesendet wird (Browser setzt Boundary automatisch) + if (!(options.body instanceof FormData)) { + (headers as any)['Content-Type'] = 'application/json'; + } + + const response = await fetch(`${this.baseUrl}${endpoint}`, { + ...options, + credentials: 'include', // Wichtig für httpOnly Cookies + headers + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({ message: 'Ein Fehler ist aufgetreten' })); + return { error: errorData.message || `HTTP Error ${response.status}` }; + } + + const data = await response.json(); + return { data }; + } catch (error) { + return { error: error instanceof Error ? error.message : 'Netzwerkfehler' }; + } + } + + async post(endpoint: string, body: any) { + return this.request(endpoint, { + method: 'POST', + body: JSON.stringify(body) + }); + } + + // Auth Endpoints + async login(username: string, password: string) { + return this.request<{ message: string; user: User }>('/auth/login', { + method: 'POST', + body: JSON.stringify({ username, password }) + }); + } + + async register(email: string, username: string, password: string, role: 'Student' | 'Lehrer' = 'Student') { + return this.request<{ message: string; user: User }>('/auth/register', { + method: 'POST', + body: JSON.stringify({ email, username, password, role }) + }); + } + + async logout() { + return this.request<{ message: string }>('/auth/logout', { + method: 'POST' + }); + } + + async getMe() { + return this.request('/auth/me'); + } + + // Users Endpoints (nur für Lehrer) + async getUsers() { + return this.request('/users'); + } + + async getUser(id: number) { + return this.request(`/users/${id}`); + } + + async updateUser(id: number, data: Partial) { + return this.request(`/users/${id}`, { + method: 'PATCH', + body: JSON.stringify(data) + }); + } + + async deleteUser(id: number) { + return this.request<{ message: string }>(`/users/${id}`, { + method: 'DELETE' + }); + } + + // Files Endpoints + async uploadFile(file: File, timetableEntryId?: number) { + const formData = new FormData(); + formData.append('file', file); + if (timetableEntryId) { + formData.append('timetableEntryId', timetableEntryId.toString()); + } + + return this.request('/files/upload', { + method: 'POST', + body: formData + }); + } + + async getGeneralFiles() { + return this.request('/files'); + } + + async getLessonFiles(lessonId: number) { + return this.request(`/files/lesson/${lessonId}`); + } + + async deleteFile(id: number) { + return this.request<{ success: boolean }>(`/files/${id}`, { + method: 'DELETE' + }); + } + + getDownloadUrl(id: number) { + return `${this.baseUrl}/files/download/${id}`; + } + + // Subjects Endpoints + async getSubjects() { + return this.request('/subjects'); + } + + async getSubject(id: number) { + return this.request(`/subjects/${id}`); + } + + async createSubject(data: CreateSubjectDto) { + return this.request('/subjects', { + method: 'POST', + body: JSON.stringify(data) + }); + } + + async updateSubject(id: number, data: Partial) { + return this.request(`/subjects/${id}`, { + method: 'PUT', + body: JSON.stringify(data) + }); + } + + async deleteSubject(id: number) { + return this.request<{ message: string }>(`/subjects/${id}`, { + method: 'DELETE' + }); + } + + // Rooms Endpoints + async getRooms() { + return this.request('/rooms'); + } + + async getRoom(id: number) { + return this.request(`/rooms/${id}`); + } + + async createRoom(data: CreateRoomDto) { + return this.request('/rooms', { + method: 'POST', + body: JSON.stringify(data) + }); + } + + async updateRoom(id: number, data: Partial) { + return this.request(`/rooms/${id}`, { + method: 'PUT', + body: JSON.stringify(data) + }); + } + + async deleteRoom(id: number) { + return this.request<{ message: string }>(`/rooms/${id}`, { + method: 'DELETE' + }); + } + + // Timetable Endpoints + async getTimetable(weekNumber?: number, year?: number) { + const params = new URLSearchParams(); + if (weekNumber !== undefined) params.append('weekNumber', weekNumber.toString()); + if (year !== undefined) params.append('year', year.toString()); + const query = params.toString() ? `?${params.toString()}` : ''; + return this.request(`/timetable${query}`); + } + + async createTimetableEntry(data: CreateTimetableEntryDto) { + return this.request('/timetable', { + method: 'POST', + body: JSON.stringify(data) + }); + } + + async updateTimetableEntry(id: number, data: Partial) { + return this.request(`/timetable/${id}`, { + method: 'PUT', + body: JSON.stringify(data) + }); + } + + async deleteTimetableEntry(id: number) { + return this.request<{ message: string }>(`/timetable/${id}`, { + method: 'DELETE' + }); + } + + // Feedback Endpoints + async getFeedback() { + return this.request('/feedback/all'); + } + + async getFeedbackByLesson(lessonId: string) { + return this.request(`/feedback/lesson/${lessonId}`); + } + + async createFeedback(data: CreateFeedbackDto) { + return this.request('/feedback', { + method: 'POST', + body: JSON.stringify(data) + }); + } + + async getTeachers() { + return this.request('/users/teachers'); + } + + // Grades Endpoints + async getGradesByTimetableEntry(timetableEntryId: number, weekNumber: number, year: number) { + return this.request(`/grades/timetable/${timetableEntryId}?weekNumber=${weekNumber}&year=${year}`); + } + + async getGrades(filters?: { studentId?: number; timetableEntryId?: number; teacherId?: number }) { + const params = new URLSearchParams(); + if (filters?.studentId) params.append('studentId', filters.studentId.toString()); + if (filters?.timetableEntryId) params.append('timetableEntryId', filters.timetableEntryId.toString()); + if (filters?.teacherId) params.append('teacherId', filters.teacherId.toString()); + const query = params.toString() ? `?${params.toString()}` : ''; + return this.request(`/grades${query}`); + } + + async getAverageGrade(timetableEntryId: number) { + return this.request(`/grades/average/${timetableEntryId}`); + } + + async getStudentOverview() { + return this.request('/grades/overview'); + } + + async createGrade(data: CreateGradeDto) { + return this.request('/grades', { + method: 'POST', + body: JSON.stringify(data) + }); + } + + async updateGrade(id: number, data: Partial) { + return this.request(`/grades/${id}`, { + method: 'PUT', + body: JSON.stringify(data) + }); + } + + async deleteGrade(id: number) { + return this.request<{ message: string }>(`/grades/${id}`, { + method: 'DELETE' + }); + } + + // Lehrer: Komplette Notenübersicht für alle Fächer + async getTeacherGradesOverview() { + return this.request('/grades/teacher/overview'); + } + + // Lehrer: Detaillierte Notenansicht für ein spezifisches Fach + async getSubjectGradeDetails(timetableEntryId: number) { + return this.request(`/grades/subject/${timetableEntryId}`); + } + + // Lehrer: Detaillierte Notenansicht für alle Stundenplan-Einträge mit gleichem Fachnamen + async getSubjectGradeDetailsByName(subjectName: string) { + return this.request(`/grades/subject-by-name/${encodeURIComponent(subjectName)}`); + } + + // Attendance Endpoints + async getPresentStudents() { + return this.request('/attendance/present'); + } +} + +// Types +export interface PresentStudent { + studentId: number; + name: string; + className: string; + checkInTime: string; + minutesPresent: number; +} + +export interface AbsentStudent { + studentId: number; + name: string; + className: string; +} + +export interface AttendanceStatus { + date: string; + time: string; + presentStudents: PresentStudent[]; + absentStudents: AbsentStudent[]; + totalPresent: number; + totalAbsent: number; +} + +export interface User { + id: number; + email: string; + username: string; + role: 'Student' | 'Lehrer'; + createdAt?: string; + updatedAt?: string; +} + +export interface UpdateUserDto { + email?: string; + username?: string; + password?: string; + role?: 'Student' | 'Lehrer'; +} + +export interface Subject { + id: number; + name: string; // z.B. "Mathematik", "Deutsch" + abbreviation?: string | null; // z.B. "Mathe", "DE" + color?: string | null; // z.B. "#FF5733" + createdAt?: string; + updatedAt?: string; +} + +export interface CreateSubjectDto { + name: string; + abbreviation?: string; + color?: string; +} + +export interface Room { + id: number; + number: string; // z.B. "A101", "B203" + building?: string | null; // z.B. "Hauptgebäude" + capacity?: number | null; + equipment?: string | null; // z.B. "Beamer, Whiteboard" + createdAt?: string; + updatedAt?: string; +} + +export interface CreateRoomDto { + number: string; + building?: string; + capacity?: number; + equipment?: string; +} + +export interface TimetableEntry { + id: number; + dayOfWeek: number; // 1-5 (Montag-Freitag) + startTime: string; // Format: "HH:MM" + endTime: string; // Format: "HH:MM" + subjectId: number; + subject: Subject; + teacherId?: number | null; + teacher?: User | null; + roomId?: number | null; + room?: Room | null; + weekNumber?: number; // 1-53 (Kalenderwoche) + year?: number; // Jahr (z.B. 2025) + isRecurring?: boolean; // true = wiederholt sich jede Woche + allowStudentUploads?: boolean; + createdAt?: string; + updatedAt?: string; +} + +export interface CreateTimetableEntryDto { + dayOfWeek: number; + startTime: string; + endTime: string; + subjectId?: number; + subjectName?: string; + teacherId?: number; + roomId?: number; + roomNumber?: string; + weekNumber?: number; + year?: number; + isRecurring?: boolean; + allowStudentUploads?: boolean; +} + +export interface FileEntity { + id: number; + filename: string; + path: string; + mimetype: string; + size: number; + uploadedById: number; + uploadedBy: { + id?: number; + username: string; + role: string; + }; + timetableEntryId?: number | null; + createdAt: string; +} + +export interface Feedback { + id: number; + studentId: number; + student?: { + id: number; + username: string; + email: string; + }; + teacherId: number; + teacher?: { + id: number; + username: string; + email: string; + }; + lessonId: number; + timetableEntry?: { + id: number; + subject: Subject; + teacher?: User | null; + room?: Room | null; + dayOfWeek: number; + startTime: string; + endTime: string; + }; + lessonDate: string; + overallRating: number; + categories?: { + clarity?: number; + pace?: number; + interaction?: number; + materials?: number; + atmosphere?: number; + }; + whatWasGood?: string; + whatCanImprove?: string; + additionalComments?: string; + isAnonymous: boolean; + allowTeacherResponse: boolean; + teacherResponse?: string | null; + teacherRespondedAt?: string | null; + createdAt?: string; +} + +export interface CreateFeedbackDto { + studentId: number; + teacherId: number; + lessonId: number; + lessonDate: Date | string; + overallRating: number; + categories?: { + clarity?: number; + pace?: number; + interaction?: number; + materials?: number; + atmosphere?: number; + }; + whatWasGood?: string; + whatCanImprove?: string; + additionalComments?: string; + isAnonymous?: boolean; + allowTeacherResponse?: boolean; +} + +export interface Grade { + id: number; + studentId: number; + student?: { + id: number; + username: string; + email: string; + }; + timetableEntryId: number; + timetableEntry?: { + id: number; + subject: Subject; + teacher?: User | null; + room?: Room | null; + dayOfWeek?: number; + startTime?: string; + endTime?: string; + }; + weekNumber: number; + year: number; + grade: number; + gradeType: string; + weight?: number; + title?: string; + description?: string; + maxPoints?: number; + achievedPoints?: number; + date: string; + teacherId: number; + teacher?: { + id: number; + username: string; + email: string; + }; + createdAt?: string; + updatedAt?: string; +} + +export interface CreateGradeDto { + studentId: number; + timetableEntryId: number; + weekNumber: number; + year: number; + grade: number; + gradeType: string; + weight?: number; + title?: string; + description?: string; + maxPoints?: number; + achievedPoints?: number; + date: string; + teacherId: number; +} + +export interface GradeAverage { + average: number; + gradeCount: number; + grades: { + id: number; + grade: number; + gradeType: string; + weight?: number; + title?: string; + date: string; + }[]; +} + +export interface SubjectOverview { + timetableEntryId: number; + subject: Subject; + teacher?: User | null; + average: number; + gradeCount: number; + latestGrade: Grade; +} + +// Lehrer-spezifische Notenübersicht +export interface TeacherGradesOverview { + subject: Subject; + timetableEntryIds: number[]; // Alle IDs für dieses Fach + timetableEntries: Array<{ + id: number; + teacher?: User | null; + dayOfWeek: number; + startTime: string; + endTime: string; + }>; // Alle Stundenplan-Einträge für dieses Fach + studentCount: number; + totalGrades: number; + students: StudentGradesSummary[]; +} + +export interface StudentGradesSummary { + student: { + id: number; + username: string; + email: string; + }; + average: number | null; + gradeCount: number; + grades: Grade[]; +} + +// Detaillierte Notenansicht für ein Fach +export interface SubjectGradeDetails { + timetableEntry: { + id: number; + subject: Subject; + teacher?: User | null; + room?: Room | null; + dayOfWeek: number; + startTime: string; + endTime: string; + }; + students: StudentWithGrades[]; + totalStudents: number; + totalGrades: number; +} + +export interface SubjectGradeDetailsByName { + subject: Subject; + timetableEntries: Array<{ + id: number; + teacher?: User | null; + room?: Room | null; + dayOfWeek: number; + startTime: string; + endTime: string; + }>; + students: StudentWithGrades[]; + totalStudents: number; + totalGrades: number; +} + +export interface StudentWithGrades { + student: { + id: number; + username: string; + email: string; + }; + average: number | null; + totalWeight: number; + gradeCount: number; + grades: GradeDetail[]; +} + +export interface GradeDetail { + id: number; + grade: number; + gradeType: string; + weight: number; + title?: string; + description?: string; + maxPoints?: number; + achievedPoints?: number; + date: string; + weekNumber: number; + year: number; +} + +export const api = new ApiClient(API_BASE_URL); diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/assets/favicon.svg b/Smartes-Klassenzimmer-Frontend/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/components/FileUploader.svelte b/Smartes-Klassenzimmer-Frontend/src/lib/components/FileUploader.svelte new file mode 100644 index 0000000..6deff7e --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/lib/components/FileUploader.svelte @@ -0,0 +1,104 @@ + + + + +
document.getElementById('fileInput')?.click()} + role="button" + tabindex="0" + onkeydown={(e) => e.key === 'Enter' && document.getElementById('fileInput')?.click()} +> + + + {#if uploading} +
Uploading...
+ {:else} +
+ + + +

Dateien hierher ziehen oder klicken

+

Alle Dateitypen erlaubt

+
+ {/if} + + {#if error} +
{error}
+ {/if} +
diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/components/Navigation.svelte b/Smartes-Klassenzimmer-Frontend/src/lib/components/Navigation.svelte new file mode 100644 index 0000000..ff71d05 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/lib/components/Navigation.svelte @@ -0,0 +1,135 @@ + + + + + diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/components/dashboard/AttendanceList.svelte b/Smartes-Klassenzimmer-Frontend/src/lib/components/dashboard/AttendanceList.svelte new file mode 100644 index 0000000..0e0c740 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/lib/components/dashboard/AttendanceList.svelte @@ -0,0 +1,232 @@ + + +
+
+
+

Anwesenheit

+
+ +
+ +
+ {#if isLoading && !status} +
+ +

Lade Anwesenheit...

+
+ {:else if error} +
+

{error}

+ +
+ {:else if status} +
+
+ Anwesend + {status.totalPresent} +
+
+ +
+ {#if status.presentStudents.length === 0} +

Keine Schüler anwesend

+ {:else} +
    + {#each status.presentStudents as student} +
  • +
    + {student.name} + {student.className || '-'} +
    +
    + + {formatTime(student.checkInTime)} + ({student.minutesPresent} min) +
    +
  • + {/each} +
+ {/if} +
+ + + {/if} +
+
+ + diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/stores/auth.svelte.ts b/Smartes-Klassenzimmer-Frontend/src/lib/stores/auth.svelte.ts new file mode 100644 index 0000000..32e4741 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/lib/stores/auth.svelte.ts @@ -0,0 +1,80 @@ +// Auth Store mit Svelte 5 Runes +import { api, type User } from '$lib/api'; +import { goto } from '$app/navigation'; + +class AuthStore { + user = $state(null); + isLoading = $state(true); + error = $state(null); + + get isAuthenticated() { + return this.user !== null; + } + + get isTeacher() { + return this.user?.role === 'Lehrer'; + } + + async init() { + this.isLoading = true; + this.error = null; + + const response = await api.getMe(); + + if (response.data) { + this.user = response.data; + } else { + this.user = null; + } + + this.isLoading = false; + } + + async login(username: string, password: string): Promise { + this.error = null; + + const response = await api.login(username, password); + + if (response.data) { + this.user = response.data.user; + goto('/dashboard'); + return true; + } else { + this.error = response.error || 'Login fehlgeschlagen'; + return false; + } + } + + async register( + email: string, + username: string, + password: string, + role: 'Student' | 'Lehrer' = 'Student' + ): Promise { + this.error = null; + + const response = await api.register(email, username, password, role); + + if (response.data) { + this.user = response.data.user; + goto('/dashboard'); + return true; + } else { + this.error = response.error || 'Registrierung fehlgeschlagen'; + return false; + } + } + + async logout() { + await api.logout(); + this.user = null; + this.error = null; + goto('/login'); + } + + clearError() { + this.error = null; + } +} + +export const authStore = new AuthStore(); diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/stores/classroom.svelte.ts b/Smartes-Klassenzimmer-Frontend/src/lib/stores/classroom.svelte.ts new file mode 100644 index 0000000..a544c4e --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/lib/stores/classroom.svelte.ts @@ -0,0 +1,94 @@ +import { io, Socket } from 'socket.io-client'; +import { authStore } from './auth.svelte'; + +export interface RaisedHand { + userId: number; + username: string; + type: 'normal' | 'question'; + raisedAt: string; // Date string from JSON +} + +export interface ActiveStudent { + userId: number; + username: string; +} + +class ClassroomStore { + socket: Socket | null = null; + raisedHands = $state([]); + activeStudent = $state(null); + isConnected = $state(false); + + constructor() { + // Auto-connect logic could go here if we wanted it global, + // but better to call connect() from component mount or check auth + } + + connect() { + if (this.socket?.connected) return; + if (!authStore.user) return; + + this.socket = io('http://localhost:3000/api/classroom', { + withCredentials: true, + transports: ['websocket'], + }); + + this.socket.on('connect', () => { + this.isConnected = true; + console.log('Connected to classroom'); + }); + + this.socket.on('disconnect', () => { + this.isConnected = false; + console.log('Disconnected from classroom'); + }); + + this.socket.on('state-update', (data: { raisedHands: RaisedHand[]; activeStudent: ActiveStudent | null }) => { + this.raisedHands = data.raisedHands; + this.activeStudent = data.activeStudent; + }); + } + + disconnect() { + if (this.socket) { + this.socket.disconnect(); + this.socket = null; + this.isConnected = false; + } + } + + raiseHand(type: 'normal' | 'question') { + this.socket?.emit('raise-hand', { type }); + } + + lowerHand() { + this.socket?.emit('lower-hand'); + } + + lowerAllHands() { + this.socket?.emit('lower-all-hands'); + } + + pickStudent(userId: number) { + this.socket?.emit('pick-student', { userId }); + } + + pickRandom() { + this.socket?.emit('pick-random'); + } + + resetActive() { + this.socket?.emit('reset-active'); + } + + endTurn() { + this.socket?.emit('reset-active'); + } + + get myHand() { + if (!authStore.user) return undefined; + return this.raisedHands.find(h => h.userId === authStore.user?.id); + } +} + +export const classroomStore = new ClassroomStore(); diff --git a/Smartes-Klassenzimmer-Frontend/src/lib/stores/whiteboard.svelte.ts b/Smartes-Klassenzimmer-Frontend/src/lib/stores/whiteboard.svelte.ts new file mode 100644 index 0000000..3d1773a --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/lib/stores/whiteboard.svelte.ts @@ -0,0 +1,185 @@ +// Whiteboard WebSocket Store mit Svelte 5 Runes +import { io, Socket } from 'socket.io-client'; + +export interface Point { + x: number; + y: number; +} + +export type DrawingTool = 'pen' | 'eraser' | 'line' | 'rectangle' | 'circle' | 'text'; + +export interface DrawingData { + id: string; + whiteboardId: string; + tool: DrawingTool; + points: Point[]; + color?: string; + lineWidth?: number; + text?: string; + userId: string; + timestamp: number; +} + +export interface RemoteCursor { + userId: string; + x: number; + y: number; +} + +class WhiteboardStore { + socket: Socket | null = null; + connected = $state(false); + whiteboardId = $state('default-room'); + userId = $state(''); + activeUsers = $state([]); + remoteCursors = $state>(new Map()); + drawings = $state([]); + + connect(whiteboardId: string, userId: string) { + this.whiteboardId = whiteboardId; + this.userId = userId; + + // WebSocket-Verbindung herstellen + this.socket = io('http://localhost:3000/whiteboard', { + withCredentials: true, + transports: ['websocket', 'polling'] + }); + + this.setupEventListeners(); + + // Dem Whiteboard beitreten + this.socket.emit('join-whiteboard', { + whiteboardId, + userId + }); + } + + private setupEventListeners() { + if (!this.socket) return; + + this.socket.on('connect', () => { + console.log('✅ Connected to whiteboard'); + this.connected = true; + }); + + this.socket.on('disconnect', () => { + console.log('❌ Disconnected from whiteboard'); + this.connected = false; + }); + + // Empfange aktuelle Whiteboard-Daten + this.socket.on('whiteboard-state', (drawings: DrawingData[]) => { + console.log('📥 Received whiteboard state:', drawings.length, 'drawings'); + this.drawings = drawings; + }); + + // Empfange neue Zeichnungen von anderen Benutzern + this.socket.on('drawing', (drawing: DrawingData) => { + console.log('🎨 Received drawing from user:', drawing.userId); + this.drawings = [...this.drawings, drawing]; + }); + + // Benutzer ist beigetreten + this.socket.on('user-joined', (data: { userId: string; activeUsers: string[] }) => { + console.log('👋 User joined:', data.userId); + this.activeUsers = data.activeUsers; + }); + + // Benutzer hat verlassen + this.socket.on('user-left', (data: { userId: string; activeUsers: string[] }) => { + console.log('👋 User left:', data.userId); + this.activeUsers = data.activeUsers; + // Entferne Cursor des Benutzers + this.remoteCursors.delete(data.userId); + }); + + // Whiteboard wurde gelöscht + this.socket.on('whiteboard-cleared', (data: { userId: string }) => { + console.log('🗑️ Whiteboard cleared by:', data.userId); + this.drawings = []; + }); + + // Cursor-Position von anderen Benutzern + this.socket.on('cursor-position', (data: { userId: string; x: number; y: number }) => { + this.remoteCursors.set(data.userId, { + userId: data.userId, + x: data.x, + y: data.y + }); + // Trigger reaktivität + this.remoteCursors = new Map(this.remoteCursors); + }); + + // Undo wurde angefordert + this.socket.on('undo-requested', (data: { userId: string }) => { + console.log('↩️ Undo requested by:', data.userId); + // Hier könnte man Undo-Logik implementieren + }); + } + + sendDrawing(tool: DrawingTool, points: Point[], color: string, lineWidth: number, text?: string, id?: string) { + if (!this.socket || !this.connected) { + console.error('❌ Not connected to whiteboard'); + return; + } + + const drawingData: DrawingData = { + id: id || `${Date.now()}-${Math.random()}`, + whiteboardId: this.whiteboardId, + tool, + points, + color, + lineWidth, + text, + userId: this.userId, + timestamp: Date.now() + }; + + console.log('📤 Sending drawing:', tool, 'points:', points.length); + + // Füge die Zeichnung sofort lokal hinzu (optimistische Update) + this.drawings = [...this.drawings, drawingData]; + + // Sende an den Server (wird nur an andere Clients weitergeleitet) + this.socket.emit('draw', drawingData); + } + + clearWhiteboard() { + if (!this.socket || !this.connected) { + console.error('❌ Not connected to whiteboard'); + return; + } + + this.socket.emit('clear-whiteboard', { + whiteboardId: this.whiteboardId, + userId: this.userId + }); + + // Lösche lokal + this.drawings = []; + } + + sendCursorPosition(x: number, y: number) { + if (!this.socket || !this.connected) return; + + this.socket.emit('cursor-move', { + whiteboardId: this.whiteboardId, + userId: this.userId, + x, + y + }); + } + + disconnect() { + if (this.socket) { + this.socket.disconnect(); + this.socket = null; + } + this.connected = false; + this.activeUsers = []; + this.remoteCursors = new Map(); + this.drawings = []; + } +} + +export const whiteboardStore = new WhiteboardStore(); diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/+layout.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/+layout.svelte new file mode 100644 index 0000000..91cfd17 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/+layout.svelte @@ -0,0 +1,30 @@ + + + + + Smartes Klassenzimmer + + +{#if showNav} + +{/if} + +{@render children()} diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/+layout.ts b/Smartes-Klassenzimmer-Frontend/src/routes/+layout.ts new file mode 100644 index 0000000..5318cdb --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/+layout.ts @@ -0,0 +1,33 @@ +import { redirect } from '@sveltejs/kit'; +import type { LayoutLoad } from './$types'; + +// Routen die NICHT authentifiziert sein müssen +const PUBLIC_ROUTES = ['/', '/login', '/register']; + +export const load: LayoutLoad = async ({ url, fetch }) => { + // Wenn auf öffentlicher Route, einfach durchlassen + if (PUBLIC_ROUTES.includes(url.pathname)) { + return {}; + } + + // Versuche den User zu laden + try { + const response = await fetch('http://localhost:3000/api/auth/me', { + credentials: 'include' + }); + + if (!response.ok) { + // Nicht authentifiziert + throw redirect(303, '/login'); + } + + const user = await response.json(); + return { user }; + } catch (error) { + // Bei Fehler zur Login-Seite umleiten + if (error instanceof Response && error.status === 303) { + throw error; + } + throw redirect(303, '/login'); + } +}; diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/+page.svelte new file mode 100644 index 0000000..7a05e35 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/+page.svelte @@ -0,0 +1,53 @@ + + +
+
+

Lade...

+
+ + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/chat/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/chat/+page.svelte new file mode 100644 index 0000000..c7e9cef --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/chat/+page.svelte @@ -0,0 +1,1323 @@ + + +
+
+
+ + + + +
+ {#if currentGroup} +
+
+

{currentGroup.name}

+ {#if currentGroup.description} +

{currentGroup.description}

+ {/if} +
+
+ {#if isTeacher()} + + {/if} +
+ {#if isConnected} + + Verbunden + {:else} + + Getrennt + {/if} +
+
+
+ + {#if error} +
+ {error} + +
+ {/if} + + {#if isLoading} +
Laden...
+ {:else} +
+ {#each messages as msg} + {#if msg.type === 'system'} +
+ {msg.text} +
+ {:else} +
+
+ {msg.username} + {formatTime(msg.timestamp)} +
+
{msg.message}
+
+ {/if} + {/each} +
+ {/if} + +
+ + +
+ {:else} +
+

Willkommen im Chat!

+

Wähle eine Gruppe aus der Seitenleiste, um zu starten.

+
+ {/if} +
+
+
+
+ + +{#if showCreateGroupModal} + +{/if} + + +{#if showMembersModal && currentGroup} + +{/if} + + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/dashboard/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/dashboard/+page.svelte new file mode 100644 index 0000000..73a3105 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/dashboard/+page.svelte @@ -0,0 +1,569 @@ + + +
+
+
+

Willkommen, {authStore.user?.username}!

+ {authStore.user?.role} +
+ + {#if classroomStore.activeStudent} +
+
+ + Aktuell dran: + {classroomStore.activeStudent.username} +
+ {#if authStore.isTeacher} + + {:else if authStore.user?.id === classroomStore.activeStudent.userId} + + {/if} +
+ {/if} + +
+ +
+
+

Virtuelles Klassenzimmer

+ +
+
+ {#if authStore.isTeacher} +
+
+ + +
+ +

Meldungen ({classroomStore.raisedHands.length})

+ + {#if classroomStore.raisedHands.length === 0} +

Keine Meldungen

+ {:else} +
    + {#each classroomStore.raisedHands as hand} +
  • +
    + + {#if hand.type === 'normal'} + + {:else} + + {/if} + + {hand.username} + {new Date(hand.raisedAt).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} +
    + +
  • + {/each} +
+ {/if} +
+ {:else} + +
+ {#if classroomStore.myHand} +
+ + {#if classroomStore.myHand.type === 'normal'} + + {:else} + + {/if} + +

Du meldest dich ({classroomStore.myHand.type === 'normal' ? 'Normal' : 'Frage'})

+ +
+ {:else} +

Möchtest du etwas sagen?

+
+ + +
+ {/if} +
+ {/if} +
+
+ + {#if authStore.isTeacher} + + {/if} + +
+
+

Heutige Veranstaltungen

+ +
+
+ {#if isLoading} +
+ +
+ {:else if error} +
{error}
+ {:else} + {#each getTodayEntries() as entry} +
+
{entry.startTime} - {entry.endTime}
+
+
{entry.subject.name}
+ {#if entry.teacher} +
{entry.teacher.username}
+ {/if} + {#if entry.room} +
Raum: {entry.room.number}
+ {/if} +
+
+ {:else} +

Heute keine Veranstaltungen

+ {/each} + {/if} +
+
+ +
+
+

Kommende Veranstaltungen

+ +
+
+ {#if isLoading} +
+ +
+ {:else if error} +
{error}
+ {:else} + {#each timetableEntries as entry} +
+
+ {DAYS[entry.dayOfWeek - 1]}, {entry.startTime} +
+
+
{entry.subject.name}
+ {#if entry.room} +
Raum: {entry.room.number}
+ {/if} +
+
+ {:else} +

Keine kommenden Veranstaltungen

+ {/each} + {/if} +
+
+ + {#if authStore.isTeacher} +
+
+

Lehrer-Funktionen

+ +
+
+

Als Lehrer haben Sie Zugriff auf:

+
    +
  • Vollständige Nutzerverwaltung
  • +
  • Notenverwaltung mit Gewichtung
  • +
  • Alle Stundenpläne einsehen
  • +
  • Benutzer erstellen und bearbeiten
  • +
+
+
+ {/if} +
+
+
+ + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/feedback/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/feedback/+page.svelte new file mode 100644 index 0000000..f231dec --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/feedback/+page.svelte @@ -0,0 +1,536 @@ + + + + + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/files/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/files/+page.svelte new file mode 100644 index 0000000..e5ea7e1 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/files/+page.svelte @@ -0,0 +1,161 @@ + + +
+
+
+

File Repository

+

General documents and resources

+
+ + {#if authStore.user?.role === 'Lehrer'} + + {/if} +
+ + {#if showUploader && authStore.user?.role === 'Lehrer'} +
+
+

Upload New File

+ { + loadFiles(); + showUploader = false; + }} /> +
+
+ {/if} + + {#if loading} +
+
+
+ {:else if error} + + {:else if files.length === 0} +
+ + + +

No files found

+
+ {:else} +
+ + + + + + + + + + + + + {#each files as file} + + + + + + + + + {/each} + +
NameTypeSizeUploaded ByDateActions
+
+ + + +
{file.filename}
+
+
+
{file.mimetype}
+
+
{formatSize(file.size)}
+
+
{file.uploadedBy.username}
+
{file.uploadedBy.role}
+
+
{formatDate(file.createdAt)}
+
+ + Download + + {#if authStore.user?.role === 'Lehrer' || authStore.user?.id === file.uploadedById} + + {/if} +
+
+ {/if} +
diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/forgot-password/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/forgot-password/+page.svelte new file mode 100644 index 0000000..d007156 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/forgot-password/+page.svelte @@ -0,0 +1,106 @@ + + +
+
+

Passwort vergessen

+

Gib deine E-Mail-Adresse ein, um dein Passwort zurückzusetzen.

+ + {#if message} +
+
+ {message} +
+ +
+ {/if} + + {#if error} +
+
+ {error} +
+ +
+ {/if} + +
+
+ + +
+ + +
+ + +
+
+ + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/grades/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/grades/+page.svelte new file mode 100644 index 0000000..f0b58f4 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/grades/+page.svelte @@ -0,0 +1,1005 @@ + + +
+
+
+ {#if selectedSubject} + + {/if} +

Notenverwaltung

+

+ {#if selectedSubject} + Detailansicht: {selectedSubject.subject.name} + {:else} + Übersicht aller Fächer und vergebenen Noten + {/if} +

+
+ + {#if loading} +
+
+

Lade Noten...

+
+ {:else if error} +
+ ⚠️ + Fehler: {error} +
+ {:else if selectedSubject} + +
+
+
+

{selectedSubject.subject.name}

+
+ {#if selectedSubject.timetableEntries.length > 1} + Termine: + {#each selectedSubject.timetableEntries as entry} + {getDayName(entry.dayOfWeek)} {entry.startTime}-{entry.endTime}{entry.teacher?.username ? ` (${entry.teacher.username})` : ''} + {/each} + {:else if selectedSubject.timetableEntries[0]} + Lehrer: {selectedSubject.timetableEntries[0].teacher?.username || 'Nicht angegeben'} + Tag: {getDayName(selectedSubject.timetableEntries[0].dayOfWeek)} + Zeit: {selectedSubject.timetableEntries[0].startTime} - {selectedSubject.timetableEntries[0].endTime} + {/if} +
+
+
+
+
{selectedSubject.totalStudents}
+
Schüler
+
+
+
{selectedSubject.totalGrades}
+
Noten
+
+
+
+
+ + +
+ {#each selectedSubject.students as studentData} +
+
+
+

{studentData.student.username}

+

{studentData.student.email}

+
+
+
Gesamtnote
+
+ {studentData.average !== null ? studentData.average.toFixed(2) : 'N/A'} +
+ {#if studentData.average !== null} +
+ {getGradeName(studentData.average)} +
+ {/if} +
+ Gewichtung: {studentData.totalWeight.toFixed(1)} +
+
+
+ + +
+ + + + + + + + + + + + + + {#each studentData.grades as grade} + + + + + + + + + + {/each} + +
DatumTitelTypNoteGewichtungPunkteAktionen
+ {formatDate(grade.date)} + +
{grade.title || '-'}
+ {#if grade.description} +
{grade.description}
+ {/if} +
+ + {grade.gradeType} + + + + {grade.grade.toFixed(1)} + + + {#if editingGradeId === grade.id} +
+ + + +
+ {:else} + + {/if} +
+ {#if grade.maxPoints && grade.achievedPoints !== null && grade.achievedPoints !== undefined} + {grade.achievedPoints}/{grade.maxPoints} + {:else} + - + {/if} + + +
+ + {#if studentData.grades.length === 0} +
Keine Noten vorhanden
+ {/if} +
+
+ {/each} +
+ + {#if selectedSubject.students.length === 0} +
+ 📝 +

Keine Schüler mit Noten in diesem Fach

+
+ {/if} + {:else} + +
+ {#each subjects as subject} + + {/each} +
+ + {#if subjects.length === 0} +
+ 📚 +

Keine Fächer mit Noten vorhanden

+
+ {/if} + {/if} +
+
+ + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/login/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/login/+page.svelte new file mode 100644 index 0000000..7fd756e --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/login/+page.svelte @@ -0,0 +1,111 @@ + + + + + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/register/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/register/+page.svelte new file mode 100644 index 0000000..c65d180 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/register/+page.svelte @@ -0,0 +1,181 @@ + + +
+
+

Registrieren

+

Erstellen Sie Ihr Konto

+ + {#if authStore.error || validationError} +
+
+ {authStore.error || validationError} +
+ +
+ {/if} + +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + +
+
+ + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/reset-password/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/reset-password/+page.svelte new file mode 100644 index 0000000..414d713 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/reset-password/+page.svelte @@ -0,0 +1,154 @@ + + +
+
+

Passwort zurücksetzen

+

Gib dein neues Passwort ein.

+ + {#if message} +
+
+ {message} +
+
+ {/if} + + {#if error} +
+
+ {error} +
+ +
+ {/if} + + {#if token && !error} +
+
+ + +
+ +
+ + +
+ + +
+ {/if} + + {#if !token && !error} +

Lade...

+ {/if} + + +
+
+ + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/timetable/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/timetable/+page.svelte new file mode 100644 index 0000000..6eda610 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/timetable/+page.svelte @@ -0,0 +1,3108 @@ + + +
+
+
+
+

Stundenplan

+

{authStore.isTeacher ? 'Verwalten Sie den Wochenstundenplan für alle' : 'Ihr Wochenstundenplan'}

+
+ {#if authStore.isTeacher} + + {/if} +
+ + {#if error} +
+ {error} + +
+ {/if} + + +
+ +
+

KW {currentWeek} - {currentYear}

+ {#if currentWeek !== getCurrentWeekNumber() || currentYear !== new Date().getFullYear()} + + {/if} +
+ +
+ + {#if isLoading} +
+

Lade Stundenplan...

+
+ {:else} +
+ + + + + {#each DAYS as day, dayIndex} + + {/each} + + + + {#each timeSlots as timeSlot} + + + {#each DAYS as _, dayIndex} + {@const entry = getEntryForTimeSlot(dayIndex + 1, timeSlot)} + {#if entry} + {#if isEntryStart(entry, timeSlot)} + + {/if} + {:else} + + {/if} + {/each} + + {/each} + +
Zeit +
+
{day}
+
{formatDate(getDateForDay(dayIndex + 1, currentWeek, currentYear))}
+
+
{timeSlot} +
openDetailModal(entry)} + onkeydown={(e) => e.key === 'Enter' && openDetailModal(entry)} + role="button" + tabindex="0" + aria-label="Details für {entry.subject.name} anzeigen" + > +
+ {entry.startTime} - {entry.endTime} +
+
{entry.subject.name}
+ {#if entry.teacher} + + {/if} + {#if entry.room} + + {/if} + {#if authStore.isTeacher} +
+ + +
+ {/if} +
+
+
+ {/if} +
+
+ + +{#if showAddModal} + +{/if} + + +{#if showEditModal && editingEntry} + +{/if} + + +{#if showDeleteConfirm && entryToDelete} + +{/if} + + +{#if showDetailModal && selectedEntry} + +{/if} + + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/users/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/users/+page.svelte new file mode 100644 index 0000000..7f43749 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/users/+page.svelte @@ -0,0 +1,632 @@ + + +
+
+
+

Nutzerverwaltung

+

Verwalten Sie alle Benutzer des Systems

+
+ + {#if error} +
+ {error} + +
+ {/if} + +
+ {#if isLoading} +
+

Lade Benutzer...

+
+ {:else if users.length === 0} +
+

Keine Benutzer gefunden

+
+ {:else} +
+ + + + + + + + + + + + + {#each users as user} + + + + + + + + + {/each} + +
IDBenutzernameE-MailRolleErstellt amAktionen
{user.id} +
{user.username}
+
{user.email} + + {user.role} + + {formatDate(user.createdAt)} +
+ + +
+
+
+ {/if} +
+
+
+ + +{#if showEditModal && editingUser} + +{/if} + + +{#if showDeleteConfirm && userToDelete} + +{/if} + + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/whiteboard/+page.svelte b/Smartes-Klassenzimmer-Frontend/src/routes/whiteboard/+page.svelte new file mode 100644 index 0000000..9ad2164 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/whiteboard/+page.svelte @@ -0,0 +1,880 @@ + + +
+
+ +
+ + + + + + + + +
+ +
+ + +
+ +
+ + + +
+ + +
+
+ Stärke: + + {lineWidth} +
+
+
+
+
+ Schrift: + + {fontSize} +
+
+ + + + + + {#each Array.from(whiteboardStore.remoteCursors.values()) as cursor} +
+ + + + {cursor.userId} +
+ {/each} + + + {#if whiteboardStore.connected} +
+
+ + Online: {whiteboardStore.activeUsers.length} +
+
+ {#each whiteboardStore.activeUsers as user} +
{user}
+ {/each} +
+
+ {/if} + + + {#if showTextInput} + + {/if} +
+
+ + diff --git a/Smartes-Klassenzimmer-Frontend/src/routes/whiteboard/+page.ts b/Smartes-Klassenzimmer-Frontend/src/routes/whiteboard/+page.ts new file mode 100644 index 0000000..a3d1578 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/src/routes/whiteboard/+page.ts @@ -0,0 +1 @@ +export const ssr = false; diff --git a/Smartes-Klassenzimmer-Frontend/static/robots.txt b/Smartes-Klassenzimmer-Frontend/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/Smartes-Klassenzimmer-Frontend/svelte.config.js b/Smartes-Klassenzimmer-Frontend/svelte.config.js new file mode 100644 index 0000000..03c17f2 --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/svelte.config.js @@ -0,0 +1,12 @@ +import adapter from '@sveltejs/adapter-node'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://svelte.dev/docs/kit/integrations + // for more information about preprocessors + preprocess: vitePreprocess(), + kit: { adapter: adapter() } +}; + +export default config; diff --git a/Smartes-Klassenzimmer-Frontend/tsconfig.json b/Smartes-Klassenzimmer-Frontend/tsconfig.json new file mode 100644 index 0000000..a5567ee --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/Smartes-Klassenzimmer-Frontend/vite.config.ts b/Smartes-Klassenzimmer-Frontend/vite.config.ts new file mode 100644 index 0000000..bbf8c7d --- /dev/null +++ b/Smartes-Klassenzimmer-Frontend/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [sveltekit()] +}); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..797ab8d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +services: + backend: + build: ./Smartes-Klassenzimmer-Backend + restart: always + ports: + - "3000:3000" + environment: + DATABASE_URL: ${DATABASE_URL} + JWT_SECRET: ${JWT_SECRET} + PORT: 3000 + SMTP_HOST: ${SMTP_HOST} + SMTP_PORT: ${SMTP_PORT} + SMTP_USER: ${SMTP_USER} + SMTP_PASS: ${SMTP_PASS} + SMTP_FROM: ${SMTP_FROM} + volumes: + - ./Smartes-Klassenzimmer-Backend/uploads:/app/uploads + + frontend: + build: ./Smartes-Klassenzimmer-Frontend + restart: always + ports: + - "80:3000" + environment: + PORT: 3000 + # URL used for server-side rendering (SSR) calls to the backend + VITE_INTERNAL_API_URL: http://backend:3000/api + # Origin URL for SvelteKit (CSRF protection) + ORIGIN: http://localhost + depends_on: + - backend \ No newline at end of file