From eceacf21174bcb914be8c7845a1659064fd8e82d Mon Sep 17 00:00:00 2001 From: philipredstone Date: Tue, 15 Apr 2025 14:46:06 +0200 Subject: [PATCH] add prettifier --- .prettierignore | 19 + .prettierrc | 10 + .vscode/settings.json | 28 + frontend/.prettierignore | 16 + frontend/.prettierrc | 12 + frontend/package-lock.json | 2853 +++++++++++++++ frontend/package.json | 5 +- frontend/src/App.tsx | 134 +- frontend/src/api/auth.ts | 120 +- frontend/src/api/network.ts | 136 +- frontend/src/api/people.ts | 154 +- frontend/src/api/relationships.ts | 141 +- frontend/src/components/FriendshipNetwork.tsx | 1578 ++++---- frontend/src/components/auth/Login.tsx | 178 +- frontend/src/components/auth/Register.tsx | 262 +- frontend/src/components/layout/Header.tsx | 116 +- .../src/components/networks/NetworkList.tsx | 548 +-- frontend/src/context/AuthContext.tsx | 148 +- frontend/src/context/NetworkContext.tsx | 218 +- frontend/src/hooks/useFriendshipNetwork.ts | 431 +-- frontend/src/main.tsx | 34 +- frontend/yarn.lock | 3230 ++++++++--------- package-lock.json | 1965 ++++++++++ package.json | 6 +- src/app.ts | 84 +- src/config/db.ts | 36 +- src/controllers/auth.controller.ts | 331 +- src/controllers/network.controller.ts | 335 +- src/controllers/people.controller.ts | 350 +- src/controllers/relationship.controller.ts | 360 +- src/middleware/auth.middleware.ts | 79 +- src/middleware/network-access.middleware.ts | 70 +- src/models/network.model.ts | 71 +- src/models/person.model.ts | 104 +- src/models/relationship.model.ts | 93 +- src/models/user.model.ts | 108 +- src/routes/auth.routes.ts | 86 +- src/routes/network.routes.ts | 92 +- src/routes/people.routes.ts | 96 +- src/routes/relationship.routes.ts | 118 +- src/server.ts | 30 +- src/types/express.d.ts | 18 +- yarn.lock | 2316 ++++++------ 43 files changed, 10946 insertions(+), 6173 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 .vscode/settings.json create mode 100644 frontend/.prettierignore create mode 100644 frontend/.prettierrc create mode 100644 frontend/package-lock.json create mode 100644 package-lock.json diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..e092c22 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,19 @@ +# Ignore build outputs +/dist +/build + +# Ignore dependencies +/node_modules + +# Ignore coverage reports +/coverage + +# Ignore logs +*.log + +# Ignore frontend (it has its own Prettier config) +/frontend + +# Ignore configuration files +.env +.env.* \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..d39207a --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "printWidth": 100, + "trailingComma": "es5", + "arrowParens": "avoid", + "endOfLine": "lf", + "bracketSpacing": true +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..eff123a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,28 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll": "explicit" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} \ No newline at end of file diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 0000000..b161fc0 --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,16 @@ +# Ignore build outputs +/dist +/build + +# Ignore dependencies +/node_modules + +# Ignore coverage reports +/coverage + +# Ignore logs +*.log + +# Ignore configuration files +.env +.env.* \ No newline at end of file diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..95fce3f --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,12 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "printWidth": 100, + "trailingComma": "es5", + "arrowParens": "avoid", + "endOfLine": "lf", + "bracketSpacing": true, + "jsxSingleQuote": false, + "bracketSameLine": false +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..65f6786 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,2853 @@ +{ + "name": "frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^1.8.4", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-router-dom": "^7.5.0" + }, + "devDependencies": { + "@types/axios": "^0.14.4", + "@types/node": "^22.14.1", + "@types/react": "^19.1.2", + "@types/react-router-dom": "^5.3.3", + "@vitejs/plugin-react": "^4.4.0", + "prettier": "^3.5.3", + "ts-node": "^10.9.2", + "typescript": "^5.8.3", + "vite": "^6.2.6", + "webpack": "^5.99.5", + "webpack-cli": "^6.0.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@ampproject/remapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", + "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.26.8", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", + "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", + "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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/@discoveryjs/json-ext": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.17.0" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "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/@rollup/rollup-linux-x64-gnu": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", + "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", + "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "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/axios": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.4.tgz", + "integrity": "sha512-9JgOaunvQdsQ/qW2OPmE5+hCeUB52lQSolecrFrthct55QekhmXEwT203s20RL+UHtCQc15y3VXpby9E7Kkh/g==", + "deprecated": "This is a stub types definition. axios provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "*" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, + "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", + "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.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/history": { + "version": "4.7.11", + "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", + "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", + "dev": true, + "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/node": { + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.1.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", + "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-router": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", + "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*" + } + }, + "node_modules/@types/react-router-dom": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", + "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/history": "^4.7.11", + "@types/react": "*", + "@types/react-router": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.4.0.tgz", + "integrity": "sha512-x/EztcTKVj+TDeANY1WjNeYsvZjZdfWRMP/KXi5Yn8BoTzpa13ZltaQqKfvWYbX8CE10GOHHdC5v86jY9x8i/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.26.10", + "@babel/plugin-transform-react-jsx-self": "^7.25.9", + "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + } + }, + "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/@webpack-cli/configtest": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "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/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.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": "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": { + "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/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/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/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "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": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "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/caniuse-lite": { + "version": "1.0.30001713", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001713.tgz", + "integrity": "sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==", + "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/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/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "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/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "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/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/electron-to-chromium": { + "version": "1.5.137", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.137.tgz", + "integrity": "sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/envinfo": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "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.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "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/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/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/esrecurse/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/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/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/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-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "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/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-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/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": 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", + "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/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "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-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/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/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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": "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/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/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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/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/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "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==", + "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/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "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.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "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/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.0.tgz", + "integrity": "sha512-estOHrRlDMKdlQa6Mj32gIks4J+AxNsYoE0DbTTxiMy2mPzZuWSDU+N85/r1IlNR7kGfznF3VCUlvc5IUO+B9g==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.0.tgz", + "integrity": "sha512-fFhGFCULy4vIseTtH5PNcY/VvDJK5gvOWcwJVHQp8JQcWVr85ENhJ3UpuF/zP1tQOIFYNRJHzXtyhU1Bdgw0RA==", + "license": "MIT", + "dependencies": { + "react-router": "7.5.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.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.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "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-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rollup": { + "version": "4.40.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", + "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.40.0", + "@rollup/rollup-android-arm64": "4.40.0", + "@rollup/rollup-darwin-arm64": "4.40.0", + "@rollup/rollup-darwin-x64": "4.40.0", + "@rollup/rollup-freebsd-arm64": "4.40.0", + "@rollup/rollup-freebsd-x64": "4.40.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", + "@rollup/rollup-linux-arm-musleabihf": "4.40.0", + "@rollup/rollup-linux-arm64-gnu": "4.40.0", + "@rollup/rollup-linux-arm64-musl": "4.40.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-gnu": "4.40.0", + "@rollup/rollup-linux-riscv64-musl": "4.40.0", + "@rollup/rollup-linux-s390x-gnu": "4.40.0", + "@rollup/rollup-linux-x64-gnu": "4.40.0", + "@rollup/rollup-linux-x64-musl": "4.40.0", + "@rollup/rollup-win32-arm64-msvc": "4.40.0", + "@rollup/rollup-win32-ia32-msvc": "4.40.0", + "@rollup/rollup-win32-x64-msvc": "4.40.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "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/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "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/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/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/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/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/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/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "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/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "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/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/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, + "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/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "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/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/vite": { + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", + "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "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/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.99.5", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.5.tgz", + "integrity": "sha512-q+vHBa6H9qwBLUlHL4Y7L0L1/LlyBKZtS9FHNCQmtayxjI5RKC9yD8gpvLeqGv5lCQp1Re04yi0MF40pf30Pvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "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.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.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-cli": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", + "colorette": "^2.0.14", + "commander": "^12.1.0", + "cross-spawn": "^7.0.3", + "envinfo": "^7.14.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^6.0.1" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.82.0" + }, + "peerDependenciesMeta": { + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-merge": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "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" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json index 42bfb74..a979d18 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -5,7 +5,9 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "format": "prettier --write \"src/**/*.{tsx,ts,js,jsx,json,css,html}\"", + "format:check": "prettier --check \"src/**/*.{tsx,ts,js,jsx,json,css,html}\"" }, "author": "", "license": "ISC", @@ -22,6 +24,7 @@ "@types/react": "^19.1.2", "@types/react-router-dom": "^5.3.3", "@vitejs/plugin-react": "^4.4.0", + "prettier": "^3.5.3", "ts-node": "^10.9.2", "typescript": "^5.8.3", "vite": "^6.2.6", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2ebcf3e..6d735be 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,67 +1,67 @@ -import React from 'react'; -import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; -import { AuthProvider, useAuth } from './context/AuthContext'; -import { NetworkProvider } from './context/NetworkContext'; -import Login from './components/auth/Login'; -import Register from './components/auth/Register'; -import NetworkList from './components/networks/NetworkList'; -import FriendshipNetwork from './components/FriendshipNetwork'; // Your existing component -import Header from './components/layout/Header'; - -// Protected route component -const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const { user, loading } = useAuth(); - - if (loading) { - return
Loading...
; - } - - if (!user) { - return ; - } - - return <>{children}; -}; - -const App: React.FC = () => { - return ( - - - -
-
-
- - } /> - } /> - - - - - } - /> - - - - - } - /> - - } /> - } /> - -
-
-
-
-
- ); -}; - -export default App; +import React from 'react'; +import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; +import { AuthProvider, useAuth } from './context/AuthContext'; +import { NetworkProvider } from './context/NetworkContext'; +import Login from './components/auth/Login'; +import Register from './components/auth/Register'; +import NetworkList from './components/networks/NetworkList'; +import FriendshipNetwork from './components/FriendshipNetwork'; // Your existing component +import Header from './components/layout/Header'; + +// Protected route component +const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const { user, loading } = useAuth(); + + if (loading) { + return
Loading...
; + } + + if (!user) { + return ; + } + + return <>{children}; +}; + +const App: React.FC = () => { + return ( + + + +
+
+
+ + } /> + } /> + + + + + } + /> + + + + + } + /> + + } /> + } /> + +
+
+
+
+
+ ); +}; + +export default App; diff --git a/frontend/src/api/auth.ts b/frontend/src/api/auth.ts index 9118d09..09f58d8 100644 --- a/frontend/src/api/auth.ts +++ b/frontend/src/api/auth.ts @@ -1,60 +1,60 @@ -import axios from 'axios'; - -const protocol = window.location.protocol; -const hostname = window.location.hostname; -const port = window.location.port; - -const API_URL = protocol + '//' + hostname + (port ? ':' + port : '') + '/api'; - -// Configure axios -axios.defaults.withCredentials = true; - -// Types -export interface RegisterData { - username: string; - email: string; - password: string; -} - -export interface LoginData { - email: string; - password: string; -} - -export interface User { - id: string; - username: string; - email: string; -} - -export interface AuthResponse { - success: boolean; - user: User; -} - -// Register user -export const register = async (data: RegisterData): Promise => { - const response = await axios.post(`${API_URL}/auth/register`, data); - return response.data.user; -}; - -// Login user -export const login = async (data: LoginData): Promise => { - const response = await axios.post(`${API_URL}/auth/login`, data); - return response.data.user; -}; - -// Logout user -export const logout = async (): Promise => { - await axios.post(`${API_URL}/auth/logout`); -}; - -// Get current user -export const getCurrentUser = async (): Promise => { - try { - const response = await axios.get(`${API_URL}/auth/me`); - return response.data.user; - } catch (error) { - return null; - } -}; \ No newline at end of file +import axios from 'axios'; + +const protocol = window.location.protocol; +const hostname = window.location.hostname; +const port = window.location.port; + +const API_URL = protocol + '//' + hostname + (port ? ':' + port : '') + '/api'; + +// Configure axios +axios.defaults.withCredentials = true; + +// Types +export interface RegisterData { + username: string; + email: string; + password: string; +} + +export interface LoginData { + email: string; + password: string; +} + +export interface User { + id: string; + username: string; + email: string; +} + +export interface AuthResponse { + success: boolean; + user: User; +} + +// Register user +export const register = async (data: RegisterData): Promise => { + const response = await axios.post(`${API_URL}/auth/register`, data); + return response.data.user; +}; + +// Login user +export const login = async (data: LoginData): Promise => { + const response = await axios.post(`${API_URL}/auth/login`, data); + return response.data.user; +}; + +// Logout user +export const logout = async (): Promise => { + await axios.post(`${API_URL}/auth/logout`); +}; + +// Get current user +export const getCurrentUser = async (): Promise => { + try { + const response = await axios.get(`${API_URL}/auth/me`); + return response.data.user; + } catch (error) { + return null; + } +}; diff --git a/frontend/src/api/network.ts b/frontend/src/api/network.ts index 73605df..60f107e 100644 --- a/frontend/src/api/network.ts +++ b/frontend/src/api/network.ts @@ -1,64 +1,72 @@ -import axios from 'axios'; - -const protocol = window.location.protocol; -const hostname = window.location.hostname; -const port = window.location.port; - -const API_URL = protocol + '//' + hostname + (port ? ':' + port : '') + '/api'; - -// Types -export interface NetworkOwner { - _id: string; - username: string; -} - -export interface Network { - _id: string; - name: string; - description?: string; - owner: string | NetworkOwner; // Can be either string ID or populated object - isPublic: boolean; - createdAt: string; - updatedAt: string; -} - -export interface CreateNetworkData { - name: string; - description?: string; - isPublic?: boolean; -} - -export interface UpdateNetworkData { - name?: string; - description?: string; - isPublic?: boolean; -} - -// Get all networks for current user -export const getUserNetworks = async (): Promise => { - const response = await axios.get<{ success: boolean; data: Network[] }>(`${API_URL}/networks`); - return response.data.data; -}; - -// Create a new network -export const createNetwork = async (data: CreateNetworkData): Promise => { - const response = await axios.post<{ success: boolean; data: Network }>(`${API_URL}/networks`, data); - return response.data.data; -}; - -// Get a specific network -export const getNetwork = async (id: string): Promise => { - const response = await axios.get<{ success: boolean; data: Network }>(`${API_URL}/networks/${id}`); - return response.data.data; -}; - -// Update a network -export const updateNetwork = async (id: string, data: UpdateNetworkData): Promise => { - const response = await axios.put<{ success: boolean; data: Network }>(`${API_URL}/networks/${id}`, data); - return response.data.data; -}; - -// Delete a network -export const deleteNetwork = async (id: string): Promise => { - await axios.delete(`${API_URL}/networks/${id}`); -}; \ No newline at end of file +import axios from 'axios'; + +const protocol = window.location.protocol; +const hostname = window.location.hostname; +const port = window.location.port; + +const API_URL = protocol + '//' + hostname + (port ? ':' + port : '') + '/api'; + +// Types +export interface NetworkOwner { + _id: string; + username: string; +} + +export interface Network { + _id: string; + name: string; + description?: string; + owner: string | NetworkOwner; // Can be either string ID or populated object + isPublic: boolean; + createdAt: string; + updatedAt: string; +} + +export interface CreateNetworkData { + name: string; + description?: string; + isPublic?: boolean; +} + +export interface UpdateNetworkData { + name?: string; + description?: string; + isPublic?: boolean; +} + +// Get all networks for current user +export const getUserNetworks = async (): Promise => { + const response = await axios.get<{ success: boolean; data: Network[] }>(`${API_URL}/networks`); + return response.data.data; +}; + +// Create a new network +export const createNetwork = async (data: CreateNetworkData): Promise => { + const response = await axios.post<{ success: boolean; data: Network }>( + `${API_URL}/networks`, + data + ); + return response.data.data; +}; + +// Get a specific network +export const getNetwork = async (id: string): Promise => { + const response = await axios.get<{ success: boolean; data: Network }>( + `${API_URL}/networks/${id}` + ); + return response.data.data; +}; + +// Update a network +export const updateNetwork = async (id: string, data: UpdateNetworkData): Promise => { + const response = await axios.put<{ success: boolean; data: Network }>( + `${API_URL}/networks/${id}`, + data + ); + return response.data.data; +}; + +// Delete a network +export const deleteNetwork = async (id: string): Promise => { + await axios.delete(`${API_URL}/networks/${id}`); +}; diff --git a/frontend/src/api/people.ts b/frontend/src/api/people.ts index 9c39dc5..c7feb7f 100644 --- a/frontend/src/api/people.ts +++ b/frontend/src/api/people.ts @@ -1,77 +1,77 @@ -import axios from 'axios'; - -const protocol = window.location.protocol; -const hostname = window.location.hostname; -const port = window.location.port; - -const API_URL = protocol + '//' + hostname + (port ? ':' + port : '') + '/api'; - -// Types -export interface Person { - _id: string; - firstName: string; - lastName: string; - birthday?: string; - network: string; - position: { - x: number; - y: number; - }; - createdAt: string; - updatedAt: string; -} - -export interface CreatePersonData { - firstName: string; - lastName: string; - birthday?: string; - position?: { - x: number; - y: number; - }; -} - -export interface UpdatePersonData { - firstName?: string; - lastName?: string; - birthday?: string | null; - position?: { - x: number; - y: number; - }; -} - -// Get all people in a network -export const getPeople = async (networkId: string): Promise => { - const response = await axios.get<{ success: boolean; data: Person[] }>( - `${API_URL}/networks/${networkId}/people` - ); - return response.data.data; -}; - -// Add a person to the network -export const addPerson = async (networkId: string, data: CreatePersonData): Promise => { - const response = await axios.post<{ success: boolean; data: Person }>( - `${API_URL}/networks/${networkId}/people`, - data - ); - return response.data.data; -}; - -// Update a person -export const updatePerson = async ( - networkId: string, - personId: string, - data: UpdatePersonData -): Promise => { - const response = await axios.put<{ success: boolean; data: Person }>( - `${API_URL}/networks/${networkId}/people/${personId}`, - data - ); - return response.data.data; -}; - -// Remove a person from the network -export const removePerson = async (networkId: string, personId: string): Promise => { - await axios.delete(`${API_URL}/networks/${networkId}/people/${personId}`); -}; \ No newline at end of file +import axios from 'axios'; + +const protocol = window.location.protocol; +const hostname = window.location.hostname; +const port = window.location.port; + +const API_URL = protocol + '//' + hostname + (port ? ':' + port : '') + '/api'; + +// Types +export interface Person { + _id: string; + firstName: string; + lastName: string; + birthday?: string; + network: string; + position: { + x: number; + y: number; + }; + createdAt: string; + updatedAt: string; +} + +export interface CreatePersonData { + firstName: string; + lastName: string; + birthday?: string; + position?: { + x: number; + y: number; + }; +} + +export interface UpdatePersonData { + firstName?: string; + lastName?: string; + birthday?: string | null; + position?: { + x: number; + y: number; + }; +} + +// Get all people in a network +export const getPeople = async (networkId: string): Promise => { + const response = await axios.get<{ success: boolean; data: Person[] }>( + `${API_URL}/networks/${networkId}/people` + ); + return response.data.data; +}; + +// Add a person to the network +export const addPerson = async (networkId: string, data: CreatePersonData): Promise => { + const response = await axios.post<{ success: boolean; data: Person }>( + `${API_URL}/networks/${networkId}/people`, + data + ); + return response.data.data; +}; + +// Update a person +export const updatePerson = async ( + networkId: string, + personId: string, + data: UpdatePersonData +): Promise => { + const response = await axios.put<{ success: boolean; data: Person }>( + `${API_URL}/networks/${networkId}/people/${personId}`, + data + ); + return response.data.data; +}; + +// Remove a person from the network +export const removePerson = async (networkId: string, personId: string): Promise => { + await axios.delete(`${API_URL}/networks/${networkId}/people/${personId}`); +}; diff --git a/frontend/src/api/relationships.ts b/frontend/src/api/relationships.ts index a26d81a..6da87a1 100644 --- a/frontend/src/api/relationships.ts +++ b/frontend/src/api/relationships.ts @@ -1,69 +1,72 @@ -import axios from 'axios'; - -const protocol = window.location.protocol; -const hostname = window.location.hostname; -const port = window.location.port; - -const API_URL = protocol + '//' + hostname + (port ? ':' + port : '') + '/api'; - -// Types -export interface Relationship { - _id: string; - source: string; - target: string; - type: 'freund' | 'partner' | 'familie' | 'arbeitskolleg' | 'custom'; - customType?: string; - network: string; - createdAt: string; - updatedAt: string; -} - -export interface CreateRelationshipData { - source: string; - target: string; - type: 'freund' | 'partner' | 'familie' | 'arbeitskolleg' | 'custom'; - customType?: string; -} - -export interface UpdateRelationshipData { - type?: 'freund' | 'partner' | 'familie' | 'arbeitskolleg' | 'custom'; - customType?: string; -} - -// Get all relationships in a network -export const getRelationships = async (networkId: string): Promise => { - const response = await axios.get<{ success: boolean; data: Relationship[] }>( - `${API_URL}/networks/${networkId}/relationships` - ); - return response.data.data; -}; - -// Add a relationship to the network -export const addRelationship = async ( - networkId: string, - data: CreateRelationshipData -): Promise => { - const response = await axios.post<{ success: boolean; data: Relationship }>( - `${API_URL}/networks/${networkId}/relationships`, - data - ); - return response.data.data; -}; - -// Update a relationship -export const updateRelationship = async ( - networkId: string, - relationshipId: string, - data: UpdateRelationshipData -): Promise => { - const response = await axios.put<{ success: boolean; data: Relationship }>( - `${API_URL}/networks/${networkId}/relationships/${relationshipId}`, - data - ); - return response.data.data; -}; - -// Remove a relationship -export const removeRelationship = async (networkId: string, relationshipId: string): Promise => { - await axios.delete(`${API_URL}/networks/${networkId}/relationships/${relationshipId}`); -}; \ No newline at end of file +import axios from 'axios'; + +const protocol = window.location.protocol; +const hostname = window.location.hostname; +const port = window.location.port; + +const API_URL = protocol + '//' + hostname + (port ? ':' + port : '') + '/api'; + +// Types +export interface Relationship { + _id: string; + source: string; + target: string; + type: 'freund' | 'partner' | 'familie' | 'arbeitskolleg' | 'custom'; + customType?: string; + network: string; + createdAt: string; + updatedAt: string; +} + +export interface CreateRelationshipData { + source: string; + target: string; + type: 'freund' | 'partner' | 'familie' | 'arbeitskolleg' | 'custom'; + customType?: string; +} + +export interface UpdateRelationshipData { + type?: 'freund' | 'partner' | 'familie' | 'arbeitskolleg' | 'custom'; + customType?: string; +} + +// Get all relationships in a network +export const getRelationships = async (networkId: string): Promise => { + const response = await axios.get<{ success: boolean; data: Relationship[] }>( + `${API_URL}/networks/${networkId}/relationships` + ); + return response.data.data; +}; + +// Add a relationship to the network +export const addRelationship = async ( + networkId: string, + data: CreateRelationshipData +): Promise => { + const response = await axios.post<{ success: boolean; data: Relationship }>( + `${API_URL}/networks/${networkId}/relationships`, + data + ); + return response.data.data; +}; + +// Update a relationship +export const updateRelationship = async ( + networkId: string, + relationshipId: string, + data: UpdateRelationshipData +): Promise => { + const response = await axios.put<{ success: boolean; data: Relationship }>( + `${API_URL}/networks/${networkId}/relationships/${relationshipId}`, + data + ); + return response.data.data; +}; + +// Remove a relationship +export const removeRelationship = async ( + networkId: string, + relationshipId: string +): Promise => { + await axios.delete(`${API_URL}/networks/${networkId}/relationships/${relationshipId}`); +}; diff --git a/frontend/src/components/FriendshipNetwork.tsx b/frontend/src/components/FriendshipNetwork.tsx index 865fbcc..7c871f6 100644 --- a/frontend/src/components/FriendshipNetwork.tsx +++ b/frontend/src/components/FriendshipNetwork.tsx @@ -1,771 +1,807 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; -import { useFriendshipNetwork } from '../hooks/useFriendshipNetwork'; -import { useNetworks } from '../context/NetworkContext'; - -const FriendshipNetwork: React.FC = () => { - const { id } = useParams<{ id: string }>(); - const { networks } = useNetworks(); - const navigate = useNavigate(); - - const { - people, - relationships, - loading, - error, - createPerson, - updatePerson, - deletePerson, - createRelationship, - updateRelationship, - deleteRelationship - } = useFriendshipNetwork(id || null); - - // Local state for the UI - const [selectedNode, setSelectedNode] = useState(null); - const [popupInfo, setPopupInfo] = useState(null); - const [newPerson, setNewPerson] = useState({ - firstName: '', - lastName: '', - birthday: '' - }); - const [newRelationship, setNewRelationship] = useState({ - source: '', - targets: [] as string[], - type: 'freund', - customType: '' - }); - const svgRef = useRef(null); - const nodeRefs = useRef<{ [key: string]: SVGGElement | null }>({}); - const [dragging, setDragging] = useState(null); - const [dragStartPos, setDragStartPos] = useState({ x: 0, y: 0 }); - const [mousePos, setMousePos] = useState({ x: 0, y: 0 }); - const [showOverrideModal, setShowOverrideModal] = useState(false); - const [overrideRelationship, setOverrideRelationship] = useState(null); - - // Get current network info - const currentNetwork = networks.find(network => network._id === id); - - // Redirect if network not found - useEffect(() => { - if (!loading && !currentNetwork && networks.length > 0) { - navigate('/networks'); - } - }, [currentNetwork, networks, loading, navigate]); - - // Add a new person to the network - const handleAddPerson = async () => { - if (newPerson.firstName.trim() === '' || newPerson.lastName.trim() === '') { - alert('Please enter both first and last name'); - return; - } - - try { - await createPerson({ - firstName: newPerson.firstName.trim(), - lastName: newPerson.lastName.trim(), - birthday: newPerson.birthday || undefined, - position: { - x: 100 + Math.random() * 400, - y: 100 + Math.random() * 300 - } - }); - - setNewPerson({ - firstName: '', - lastName: '', - birthday: '' - }); - } catch (error) { - console.error('Error adding person:', error); - alert('Failed to add person.'); - } - }; - - // Add new relationships between source person and multiple target people - const handleAddRelationship = async () => { - const { source, targets, type, customType } = newRelationship; - - if (source === '' || targets.length === 0) { - alert('Please select source and at least one target person'); - return; - } - - const actualType = type === 'custom' ? customType.trim() : type; - - if (type === 'custom' && customType.trim() === '') { - alert('Please enter a custom relationship type'); - return; - } - - // Check if any relationships already exist - const existingRelationships: any[] = []; - targets.forEach(target => { - if (source !== target) { - const existingEdge = relationships.find(edge => - (edge.source === source && edge.target === target) || - (edge.source === target && edge.target === source) - ); - - if (existingEdge) { - existingRelationships.push({ - source, - target, - existingType: existingEdge.type, - newType: actualType, - edgeId: existingEdge.id - }); - } - } - }); - - if (existingRelationships.length > 0) { - // Show override modal - setOverrideRelationship({ - existingRelationships, - newRelationships: targets.filter(target => - source !== target && !existingRelationships.some(rel => rel.target === target) - ).map(target => ({ source, target, type: actualType })) - }); - setShowOverrideModal(true); - return; - } - - // Process each target for new relationships - const addPromises = targets.map(target => { - if (source !== target) { - return createRelationship({ - source, - target, - type: type as any, - customType: type === 'custom' ? customType : undefined - }); - } - return Promise.resolve(); - }).filter(Boolean); - - if (addPromises.length === 0) { - alert('No valid relationships to add.'); - return; - } - - try { - await Promise.all(addPromises); - setNewRelationship({ source: '', targets: [], type: 'freund', customType: '' }); - } catch (error) { - console.error('Error adding relationships:', error); - alert('Failed to add one or more relationships.'); - } - }; - - // Handle confirming relationship overrides - const handleConfirmOverride = async () => { - if (!overrideRelationship) return; - - const { existingRelationships, newRelationships } = overrideRelationship; - - try { - // Remove existing relationships that will be overridden - await Promise.all(existingRelationships.map(rel => deleteRelationship(rel.edgeId))); - - // Add new overridden relationships - await Promise.all(existingRelationships.map(rel => - createRelationship({ - source: rel.source, - target: rel.target, - type: rel.newType as any, - customType: rel.newType === 'custom' ? rel.customType : undefined - }) - )); - - // Add completely new relationships - await Promise.all(newRelationships.map(rel => - createRelationship({ - source: rel.source, - target: rel.target, - type: rel.type as any, - customType: rel.type === 'custom' ? rel.customType : undefined - }) - )); - - setShowOverrideModal(false); - setOverrideRelationship(null); - setNewRelationship({ source: '', targets: [], type: 'freund', customType: '' }); - } catch (error) { - console.error('Error overriding relationships:', error); - alert('Failed to override relationships.'); - } - }; - - // Handle canceling relationship overrides - const handleCancelOverride = async () => { - // If there are new relationships that don't need overrides, add those - if (overrideRelationship && overrideRelationship.newRelationships.length > 0) { - try { - await Promise.all(overrideRelationship.newRelationships.map(rel => - createRelationship({ - source: rel.source, - target: rel.target, - type: rel.type as any, - customType: rel.type === 'custom' ? rel.customType : undefined - }) - )); - } catch (error) { - console.error('Error adding new relationships:', error); - } - } - - setShowOverrideModal(false); - setOverrideRelationship(null); - }; - - // Handle multiple selections in the targets dropdown - const handleTargetChange = (e: React.ChangeEvent) => { - const selectedOptions = Array.from(e.target.selectedOptions, option => option.value); - setNewRelationship({...newRelationship, targets: selectedOptions}); - }; - - // Handle node drag start - const handleMouseDown = (e: React.MouseEvent, id: string) => { - if (svgRef.current) { - const node = people.find(n => n.id === id); - if (!node) return; - - setDragging(id); - setDragStartPos({ ...node.position }); - setMousePos({ x: e.clientX, y: e.clientY }); - - e.stopPropagation(); - e.preventDefault(); - } - }; - - // Handle node dragging - const handleMouseMove = (e: React.MouseEvent) => { - if (dragging && svgRef.current) { - const dx = e.clientX - mousePos.x; - const dy = e.clientY - mousePos.y; - - const newX = dragStartPos.x + dx; - const newY = dragStartPos.y + dy; - - // Update node position in the UI immediately - const updatedPeople = people.map(node => - node.id === dragging - ? { - ...node, - position: { x: newX, y: newY } - } - : node - ); - - // We don't actually update the state here for performance reasons - // Instead, we update the DOM directly - const draggedNode = nodeRefs.current[dragging]; - if (draggedNode) { - draggedNode.setAttribute('transform', `translate(${newX}, ${newY})`); - } - } - }; - - // Handle node drag end - const handleMouseUp = async () => { - if (dragging) { - const node = people.find(n => n.id === dragging); - if (node) { - // Get the final position from the DOM - const draggedNode = nodeRefs.current[dragging]; - if (draggedNode) { - const transform = draggedNode.getAttribute('transform'); - if (transform) { - const match = transform.match(/translate\(([^,]+),\s*([^)]+)\)/); - if (match) { - const x = parseFloat(match[1]); - const y = parseFloat(match[2]); - - // Save the new position to the server - try { - await updatePerson(dragging, { position: { x, y } }); - } catch (error) { - console.error('Error updating position:', error); - } - } - } - } - } - setDragging(null); - } - }; - - // Delete a node and its associated edges - const handleDeleteNode = async (id: string) => { - if (window.confirm('Are you sure you want to delete this person? All their relationships will also be deleted.')) { - try { - await deletePerson(id); - setSelectedNode(null); - setPopupInfo(null); - } catch (error) { - console.error('Error deleting person:', error); - alert('Failed to delete person.'); - } - } - }; - - // Get relationship type label - const getRelationshipLabel = (type: string) => { - switch(type) { - case 'freund': return 'Freund/in'; - case 'partner': return 'Partner/in'; - case 'familie': return 'Familie/Verwandschaft'; - case 'arbeitskolleg': return 'Arbeitskolleg/innen'; - default: return type; - } - }; - - // Remove a relationship between two people - const handleRemoveRelationship = async (edgeId: string) => { - try { - await deleteRelationship(edgeId); - - // Update popup info if it's open - if (popupInfo) { - const nodeId = popupInfo.node.id; - const nodeRelationships = relationships - .filter(edge => edge.id !== edgeId) - .filter(edge => edge.source === nodeId || edge.target === nodeId) - .map(edge => { - const otherId = edge.source === nodeId ? edge.target : edge.source; - const other = people.find(n => n.id === otherId); - return { - person: other ? `${other.firstName} ${other.lastName}` : otherId, - type: edge.type, - edgeId: edge.id - }; - }); - - setPopupInfo({ - ...popupInfo, - relationships: nodeRelationships - }); - } - } catch (error) { - console.error('Error removing relationship:', error); - alert('Failed to remove relationship.'); - } - }; - - // Show popup with person details and relationships - const showPersonDetails = (nodeId: string) => { - const node = people.find(n => n.id === nodeId); - if (!node) return; - - // Find all relationships - const nodeRelationships = relationships.filter( - edge => edge.source === nodeId || edge.target === nodeId - ).map(edge => { - const otherId = edge.source === nodeId ? edge.target : edge.source; - const other = people.find(n => n.id === otherId); - return { - person: other ? `${other.firstName} ${other.lastName}` : otherId, - type: edge.type, - edgeId: edge.id - }; - }); - - setPopupInfo({ - node, - relationships: nodeRelationships, - position: { ...node.position } - }); - }; - - // Close popup - const closePopup = () => { - setPopupInfo(null); - }; - - // Get abbreviated name for display in graph (first name + first letter of last name) - const getDisplayName = (node: any) => { - return `${node.firstName} ${node.lastName.charAt(0)}.`; - }; - - // Close popup when clicking outside - useEffect(() => { - const handleClickOutside = (e: MouseEvent) => { - if (popupInfo && - !(e.target as Element).closest('.popup') && - !dragging) { - closePopup(); - } - }; - - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [popupInfo, dragging]); - - if (loading) { - return
Loading network data...
; - } - - if (error) { - return
{error}
; - } - - return ( -
- {/* Sidebar menu */} -
-

- {currentNetwork?.name || 'Friend Network'} -

- - {/* Add Person Form */} -
-

Add Person

-
- setNewPerson({...newPerson, firstName: e.target.value})} - /> - setNewPerson({...newPerson, lastName: e.target.value})} - /> - setNewPerson({...newPerson, birthday: e.target.value})} - /> - -
-
- - {/* Add Relationship Form */} -
-

Add Relationship

-
- - - -

Hold Ctrl/Cmd to select multiple people

- - - - {newRelationship.type === 'custom' && ( - setNewRelationship({...newRelationship, customType: e.target.value})} - /> - )} - - -
-
- - {/* People List */} -
-

People ({people.length})

-
    - {people.map(node => ( -
  • - {node.firstName} {node.lastName} - -
  • - ))} -
-
- - {/* Relationships List */} -
-

All Relationships ({relationships.length})

- {relationships.length === 0 ? ( -

No relationships yet

- ) : ( -
    - {relationships.map(edge => { - const source = people.find(n => n.id === edge.source); - const target = people.find(n => n.id === edge.target); - if (!source || !target) return null; - - return ( -
  • -
    - - {source.firstName} {source.lastName.charAt(0)}. ↔ {target.firstName} {target.lastName.charAt(0)}. - -
    - {getRelationshipLabel(edge.type)} - -
    -
    -
  • - ); - })} -
- )} -
-
- - {/* Visualization Area */} -
- - {/* Edges (Relationships) */} - {relationships.map(edge => { - const source = people.find(n => n.id === edge.source); - const target = people.find(n => n.id === edge.target); - - if (!source || !target) return null; - - // Determine the line color based on relationship type - let strokeColor = '#9CA3AF'; // Default gray - if (edge.type === 'freund') strokeColor = '#3B82F6'; // Blue - if (edge.type === 'partner') strokeColor = '#EC4899'; // Pink - if (edge.type === 'familie') strokeColor = '#10B981'; // Green - if (edge.type === 'arbeitskolleg') strokeColor = '#F59E0B'; // Yellow - - return ( - - - - {getRelationshipLabel(edge.type)} - - - ); - })} - - {/* Nodes (People) */} - {people.map(node => ( - handleMouseDown(e, node.id)} - ref={el => { nodeRefs.current[node.id] = el; }} - className="cursor-grab" - > - showPersonDetails(node.id)} - /> - - {getDisplayName(node)} - - - ))} - - - {/* Person Details Popup */} - {popupInfo && ( -
(svgRef.current?.clientWidth || 0) / 2 - ? popupInfo.position.x - 260 : popupInfo.position.x + 40, - top: popupInfo.position.y > (svgRef.current?.clientHeight || 0) / 2 - ? popupInfo.position.y - 200 : popupInfo.position.y, - }} - > -
-

Person Details

- -
-
-

Name: {popupInfo.node.firstName} {popupInfo.node.lastName}

- {popupInfo.node.birthday && ( -

Birthday: {new Date(popupInfo.node.birthday).toLocaleDateString()}

- )} -
-
-

Relationships:

- {popupInfo.relationships.length === 0 ? ( -

No relationships yet

- ) : ( -
    - {popupInfo.relationships.map((rel: any, index: number) => ( -
  • -
    - {rel.person} - {getRelationshipLabel(rel.type)} -
    -
    - -
    -
  • - ))} -
- )} -
-
- )} - - {/* Override Confirmation Modal */} - {showOverrideModal && overrideRelationship && ( -
-
-

Existing Relationship(s)

-

- {overrideRelationship.existingRelationships.length === 1 - ? "There is already a relationship between these people:" - : "There are already relationships between these people:"} -

- -
    - {overrideRelationship.existingRelationships.map((rel: any, index: number) => { - const source = people.find(n => n.id === rel.source); - const target = people.find(n => n.id === rel.target); - if (!source || !target) return null; - - return ( -
  • -
    - - {source.firstName} {source.lastName} ↔ {target.firstName} {target.lastName} - -
    -
    - - Current: {getRelationshipLabel(rel.existingType)} - - - New: {getRelationshipLabel(rel.newType)} - -
    -
  • - ); - })} -
- -

Do you want to override the existing relationship(s)?

- -
- - -
-
-
- )} - - {/* Instructions */} -
-

Tip: Drag people to arrange them. Click on a person to view their details and relationships.

-
-
-
- ); -}; - -export default FriendshipNetwork; \ No newline at end of file +import React, { useState, useRef, useEffect } from 'react'; +import { useParams, useNavigate } from 'react-router-dom'; +import { useFriendshipNetwork } from '../hooks/useFriendshipNetwork'; +import { useNetworks } from '../context/NetworkContext'; + +const FriendshipNetwork: React.FC = () => { + const { id } = useParams<{ id: string }>(); + const { networks } = useNetworks(); + const navigate = useNavigate(); + + const { + people, + relationships, + loading, + error, + createPerson, + updatePerson, + deletePerson, + createRelationship, + updateRelationship, + deleteRelationship, + } = useFriendshipNetwork(id || null); + + // Local state for the UI + const [selectedNode, setSelectedNode] = useState(null); + const [popupInfo, setPopupInfo] = useState(null); + const [newPerson, setNewPerson] = useState({ + firstName: '', + lastName: '', + birthday: '', + }); + const [newRelationship, setNewRelationship] = useState({ + source: '', + targets: [] as string[], + type: 'freund', + customType: '', + }); + const svgRef = useRef(null); + const nodeRefs = useRef<{ [key: string]: SVGGElement | null }>({}); + const [dragging, setDragging] = useState(null); + const [dragStartPos, setDragStartPos] = useState({ x: 0, y: 0 }); + const [mousePos, setMousePos] = useState({ x: 0, y: 0 }); + const [showOverrideModal, setShowOverrideModal] = useState(false); + const [overrideRelationship, setOverrideRelationship] = useState(null); + + // Get current network info + const currentNetwork = networks.find(network => network._id === id); + + // Redirect if network not found + useEffect(() => { + if (!loading && !currentNetwork && networks.length > 0) { + navigate('/networks'); + } + }, [currentNetwork, networks, loading, navigate]); + + // Add a new person to the network + const handleAddPerson = async () => { + if (newPerson.firstName.trim() === '' || newPerson.lastName.trim() === '') { + alert('Please enter both first and last name'); + return; + } + + try { + await createPerson({ + firstName: newPerson.firstName.trim(), + lastName: newPerson.lastName.trim(), + birthday: newPerson.birthday || undefined, + position: { + x: 100 + Math.random() * 400, + y: 100 + Math.random() * 300, + }, + }); + + setNewPerson({ + firstName: '', + lastName: '', + birthday: '', + }); + } catch (error) { + console.error('Error adding person:', error); + alert('Failed to add person.'); + } + }; + + // Add new relationships between source person and multiple target people + const handleAddRelationship = async () => { + const { source, targets, type, customType } = newRelationship; + + if (source === '' || targets.length === 0) { + alert('Please select source and at least one target person'); + return; + } + + const actualType = type === 'custom' ? customType.trim() : type; + + if (type === 'custom' && customType.trim() === '') { + alert('Please enter a custom relationship type'); + return; + } + + // Check if any relationships already exist + const existingRelationships: any[] = []; + targets.forEach(target => { + if (source !== target) { + const existingEdge = relationships.find( + edge => + (edge.source === source && edge.target === target) || + (edge.source === target && edge.target === source) + ); + + if (existingEdge) { + existingRelationships.push({ + source, + target, + existingType: existingEdge.type, + newType: actualType, + edgeId: existingEdge.id, + }); + } + } + }); + + if (existingRelationships.length > 0) { + // Show override modal + setOverrideRelationship({ + existingRelationships, + newRelationships: targets + .filter( + target => source !== target && !existingRelationships.some(rel => rel.target === target) + ) + .map(target => ({ source, target, type: actualType })), + }); + setShowOverrideModal(true); + return; + } + + // Process each target for new relationships + const addPromises = targets + .map(target => { + if (source !== target) { + return createRelationship({ + source, + target, + type: type as any, + customType: type === 'custom' ? customType : undefined, + }); + } + return Promise.resolve(); + }) + .filter(Boolean); + + if (addPromises.length === 0) { + alert('No valid relationships to add.'); + return; + } + + try { + await Promise.all(addPromises); + setNewRelationship({ source: '', targets: [], type: 'freund', customType: '' }); + } catch (error) { + console.error('Error adding relationships:', error); + alert('Failed to add one or more relationships.'); + } + }; + + // Handle confirming relationship overrides + const handleConfirmOverride = async () => { + if (!overrideRelationship) return; + + const { existingRelationships, newRelationships } = overrideRelationship; + + try { + // Remove existing relationships that will be overridden + await Promise.all(existingRelationships.map(rel => deleteRelationship(rel.edgeId))); + + // Add new overridden relationships + await Promise.all( + existingRelationships.map(rel => + createRelationship({ + source: rel.source, + target: rel.target, + type: rel.newType as any, + customType: rel.newType === 'custom' ? rel.customType : undefined, + }) + ) + ); + + // Add completely new relationships + await Promise.all( + newRelationships.map(rel => + createRelationship({ + source: rel.source, + target: rel.target, + type: rel.type as any, + customType: rel.type === 'custom' ? rel.customType : undefined, + }) + ) + ); + + setShowOverrideModal(false); + setOverrideRelationship(null); + setNewRelationship({ source: '', targets: [], type: 'freund', customType: '' }); + } catch (error) { + console.error('Error overriding relationships:', error); + alert('Failed to override relationships.'); + } + }; + + // Handle canceling relationship overrides + const handleCancelOverride = async () => { + // If there are new relationships that don't need overrides, add those + if (overrideRelationship && overrideRelationship.newRelationships.length > 0) { + try { + await Promise.all( + overrideRelationship.newRelationships.map(rel => + createRelationship({ + source: rel.source, + target: rel.target, + type: rel.type as any, + customType: rel.type === 'custom' ? rel.customType : undefined, + }) + ) + ); + } catch (error) { + console.error('Error adding new relationships:', error); + } + } + + setShowOverrideModal(false); + setOverrideRelationship(null); + }; + + // Handle multiple selections in the targets dropdown + const handleTargetChange = (e: React.ChangeEvent) => { + const selectedOptions = Array.from(e.target.selectedOptions, option => option.value); + setNewRelationship({ ...newRelationship, targets: selectedOptions }); + }; + + // Handle node drag start + const handleMouseDown = (e: React.MouseEvent, id: string) => { + if (svgRef.current) { + const node = people.find(n => n.id === id); + if (!node) return; + + setDragging(id); + setDragStartPos({ ...node.position }); + setMousePos({ x: e.clientX, y: e.clientY }); + + e.stopPropagation(); + e.preventDefault(); + } + }; + + // Handle node dragging + const handleMouseMove = (e: React.MouseEvent) => { + if (dragging && svgRef.current) { + const dx = e.clientX - mousePos.x; + const dy = e.clientY - mousePos.y; + + const newX = dragStartPos.x + dx; + const newY = dragStartPos.y + dy; + + // Update node position in the UI immediately + const updatedPeople = people.map(node => + node.id === dragging + ? { + ...node, + position: { x: newX, y: newY }, + } + : node + ); + + // We don't actually update the state here for performance reasons + // Instead, we update the DOM directly + const draggedNode = nodeRefs.current[dragging]; + if (draggedNode) { + draggedNode.setAttribute('transform', `translate(${newX}, ${newY})`); + } + } + }; + + // Handle node drag end + const handleMouseUp = async () => { + if (dragging) { + const node = people.find(n => n.id === dragging); + if (node) { + // Get the final position from the DOM + const draggedNode = nodeRefs.current[dragging]; + if (draggedNode) { + const transform = draggedNode.getAttribute('transform'); + if (transform) { + const match = transform.match(/translate\(([^,]+),\s*([^)]+)\)/); + if (match) { + const x = parseFloat(match[1]); + const y = parseFloat(match[2]); + + // Save the new position to the server + try { + await updatePerson(dragging, { position: { x, y } }); + } catch (error) { + console.error('Error updating position:', error); + } + } + } + } + } + setDragging(null); + } + }; + + // Delete a node and its associated edges + const handleDeleteNode = async (id: string) => { + if ( + window.confirm( + 'Are you sure you want to delete this person? All their relationships will also be deleted.' + ) + ) { + try { + await deletePerson(id); + setSelectedNode(null); + setPopupInfo(null); + } catch (error) { + console.error('Error deleting person:', error); + alert('Failed to delete person.'); + } + } + }; + + // Get relationship type label + const getRelationshipLabel = (type: string) => { + switch (type) { + case 'freund': + return 'Freund/in'; + case 'partner': + return 'Partner/in'; + case 'familie': + return 'Familie/Verwandschaft'; + case 'arbeitskolleg': + return 'Arbeitskolleg/innen'; + default: + return type; + } + }; + + // Remove a relationship between two people + const handleRemoveRelationship = async (edgeId: string) => { + try { + await deleteRelationship(edgeId); + + // Update popup info if it's open + if (popupInfo) { + const nodeId = popupInfo.node.id; + const nodeRelationships = relationships + .filter(edge => edge.id !== edgeId) + .filter(edge => edge.source === nodeId || edge.target === nodeId) + .map(edge => { + const otherId = edge.source === nodeId ? edge.target : edge.source; + const other = people.find(n => n.id === otherId); + return { + person: other ? `${other.firstName} ${other.lastName}` : otherId, + type: edge.type, + edgeId: edge.id, + }; + }); + + setPopupInfo({ + ...popupInfo, + relationships: nodeRelationships, + }); + } + } catch (error) { + console.error('Error removing relationship:', error); + alert('Failed to remove relationship.'); + } + }; + + // Show popup with person details and relationships + const showPersonDetails = (nodeId: string) => { + const node = people.find(n => n.id === nodeId); + if (!node) return; + + // Find all relationships + const nodeRelationships = relationships + .filter(edge => edge.source === nodeId || edge.target === nodeId) + .map(edge => { + const otherId = edge.source === nodeId ? edge.target : edge.source; + const other = people.find(n => n.id === otherId); + return { + person: other ? `${other.firstName} ${other.lastName}` : otherId, + type: edge.type, + edgeId: edge.id, + }; + }); + + setPopupInfo({ + node, + relationships: nodeRelationships, + position: { ...node.position }, + }); + }; + + // Close popup + const closePopup = () => { + setPopupInfo(null); + }; + + // Get abbreviated name for display in graph (first name + first letter of last name) + const getDisplayName = (node: any) => { + return `${node.firstName} ${node.lastName.charAt(0)}.`; + }; + + // Close popup when clicking outside + useEffect(() => { + const handleClickOutside = (e: MouseEvent) => { + if (popupInfo && !(e.target as Element).closest('.popup') && !dragging) { + closePopup(); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [popupInfo, dragging]); + + if (loading) { + return
Loading network data...
; + } + + if (error) { + return ( +
{error}
+ ); + } + + return ( +
+ {/* Sidebar menu */} +
+

{currentNetwork?.name || 'Friend Network'}

+ + {/* Add Person Form */} +
+

Add Person

+
+ setNewPerson({ ...newPerson, firstName: e.target.value })} + /> + setNewPerson({ ...newPerson, lastName: e.target.value })} + /> + setNewPerson({ ...newPerson, birthday: e.target.value })} + /> + +
+
+ + {/* Add Relationship Form */} +
+

Add Relationship

+
+ + + +

Hold Ctrl/Cmd to select multiple people

+ + + + {newRelationship.type === 'custom' && ( + + setNewRelationship({ ...newRelationship, customType: e.target.value }) + } + /> + )} + + +
+
+ + {/* People List */} +
+

People ({people.length})

+
    + {people.map(node => ( +
  • + + {node.firstName} {node.lastName} + + +
  • + ))} +
+
+ + {/* Relationships List */} +
+

All Relationships ({relationships.length})

+ {relationships.length === 0 ? ( +

No relationships yet

+ ) : ( +
    + {relationships.map(edge => { + const source = people.find(n => n.id === edge.source); + const target = people.find(n => n.id === edge.target); + if (!source || !target) return null; + + return ( +
  • +
    + + {source.firstName} {source.lastName.charAt(0)}. ↔ {target.firstName}{' '} + {target.lastName.charAt(0)}. + +
    + + {getRelationshipLabel(edge.type)} + + +
    +
    +
  • + ); + })} +
+ )} +
+
+ + {/* Visualization Area */} +
+ + {/* Edges (Relationships) */} + {relationships.map(edge => { + const source = people.find(n => n.id === edge.source); + const target = people.find(n => n.id === edge.target); + + if (!source || !target) return null; + + // Determine the line color based on relationship type + let strokeColor = '#9CA3AF'; // Default gray + if (edge.type === 'freund') strokeColor = '#3B82F6'; // Blue + if (edge.type === 'partner') strokeColor = '#EC4899'; // Pink + if (edge.type === 'familie') strokeColor = '#10B981'; // Green + if (edge.type === 'arbeitskolleg') strokeColor = '#F59E0B'; // Yellow + + return ( + + + + {getRelationshipLabel(edge.type)} + + + ); + })} + + {/* Nodes (People) */} + {people.map(node => ( + handleMouseDown(e, node.id)} + ref={el => { + nodeRefs.current[node.id] = el; + }} + className="cursor-grab" + > + showPersonDetails(node.id)} + /> + + {getDisplayName(node)} + + + ))} + + + {/* Person Details Popup */} + {popupInfo && ( +
(svgRef.current?.clientWidth || 0) / 2 + ? popupInfo.position.x - 260 + : popupInfo.position.x + 40, + top: + popupInfo.position.y > (svgRef.current?.clientHeight || 0) / 2 + ? popupInfo.position.y - 200 + : popupInfo.position.y, + }} + > +
+

Person Details

+ +
+
+

+ Name: {popupInfo.node.firstName} {popupInfo.node.lastName} +

+ {popupInfo.node.birthday && ( +

+ Birthday: {new Date(popupInfo.node.birthday).toLocaleDateString()} +

+ )} +
+
+

Relationships:

+ {popupInfo.relationships.length === 0 ? ( +

No relationships yet

+ ) : ( +
    + {popupInfo.relationships.map((rel: any, index: number) => ( +
  • +
    + {rel.person} + {getRelationshipLabel(rel.type)} +
    +
    + +
    +
  • + ))} +
+ )} +
+
+ )} + + {/* Override Confirmation Modal */} + {showOverrideModal && overrideRelationship && ( +
+
+

Existing Relationship(s)

+

+ {overrideRelationship.existingRelationships.length === 1 + ? 'There is already a relationship between these people:' + : 'There are already relationships between these people:'} +

+ +
    + {overrideRelationship.existingRelationships.map((rel: any, index: number) => { + const source = people.find(n => n.id === rel.source); + const target = people.find(n => n.id === rel.target); + if (!source || !target) return null; + + return ( +
  • +
    + + {source.firstName} {source.lastName} ↔ {target.firstName}{' '} + {target.lastName} + +
    +
    + + Current: {getRelationshipLabel(rel.existingType)} + + + New: {getRelationshipLabel(rel.newType)} + +
    +
  • + ); + })} +
+ +

Do you want to override the existing relationship(s)?

+ +
+ + +
+
+
+ )} + + {/* Instructions */} +
+

+ Tip: Drag people to arrange them. Click on a person to view their + details and relationships. +

+
+
+
+ ); +}; + +export default FriendshipNetwork; diff --git a/frontend/src/components/auth/Login.tsx b/frontend/src/components/auth/Login.tsx index 2146b04..eb9d7c0 100644 --- a/frontend/src/components/auth/Login.tsx +++ b/frontend/src/components/auth/Login.tsx @@ -1,89 +1,89 @@ -import React, { useState } from 'react'; -import { useAuth } from '../../context/AuthContext'; -import { useNavigate } from 'react-router-dom'; - -const Login: React.FC = () => { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(null); - const [loading, setLoading] = useState(false); - const { login } = useAuth(); - const navigate = useNavigate(); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(null); - setLoading(true); - - try { - await login({ email, password }); - navigate('/networks'); - } catch (err: any) { - setError(err.response?.data?.message || 'Login failed. Please check your credentials.'); - } finally { - setLoading(false); - } - }; - - return ( -
-
-

Login

- - {error && ( -
- {error} -
- )} - -
-
- - setEmail(e.target.value)} - required - /> -
- -
- - setPassword(e.target.value)} - required - /> -
- -
- - - Register - -
-
-
-
- ); -}; - -export default Login; \ No newline at end of file +import React, { useState } from 'react'; +import { useAuth } from '../../context/AuthContext'; +import { useNavigate } from 'react-router-dom'; + +const Login: React.FC = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + const { login } = useAuth(); + const navigate = useNavigate(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + setLoading(true); + + try { + await login({ email, password }); + navigate('/networks'); + } catch (err: any) { + setError(err.response?.data?.message || 'Login failed. Please check your credentials.'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+

Login

+ + {error && ( +
+ {error} +
+ )} + +
+
+ + setEmail(e.target.value)} + required + /> +
+ +
+ + setPassword(e.target.value)} + required + /> +
+ +
+ + + Register + +
+
+
+
+ ); +}; + +export default Login; diff --git a/frontend/src/components/auth/Register.tsx b/frontend/src/components/auth/Register.tsx index e7301d0..9310fd8 100644 --- a/frontend/src/components/auth/Register.tsx +++ b/frontend/src/components/auth/Register.tsx @@ -1,131 +1,131 @@ -import React, { useState } from 'react'; -import { useAuth } from '../../context/AuthContext'; -import { useNavigate } from 'react-router-dom'; - -const Register: React.FC = () => { - const [username, setUsername] = useState(''); - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [confirmPassword, setConfirmPassword] = useState(''); - const [error, setError] = useState(null); - const [loading, setLoading] = useState(false); - const { register } = useAuth(); - const navigate = useNavigate(); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setError(null); - - // Basic validation - if (password !== confirmPassword) { - setError('Passwords do not match'); - return; - } - - if (password.length < 6) { - setError('Password must be at least 6 characters'); - return; - } - - setLoading(true); - - try { - await register({ username, email, password }); - navigate('/networks'); - } catch (err: any) { - setError(err.response?.data?.message || 'Registration failed. Please try again.'); - } finally { - setLoading(false); - } - }; - - return ( -
-
-

Register

- - {error && ( -
- {error} -
- )} - -
-
- - setUsername(e.target.value)} - required - /> -
- -
- - setEmail(e.target.value)} - required - /> -
- -
- - setPassword(e.target.value)} - required - /> -
- -
- - setConfirmPassword(e.target.value)} - required - /> -
- -
- - - Login - -
-
-
-
- ); -}; - -export default Register; \ No newline at end of file +import React, { useState } from 'react'; +import { useAuth } from '../../context/AuthContext'; +import { useNavigate } from 'react-router-dom'; + +const Register: React.FC = () => { + const [username, setUsername] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + const { register } = useAuth(); + const navigate = useNavigate(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + + // Basic validation + if (password !== confirmPassword) { + setError('Passwords do not match'); + return; + } + + if (password.length < 6) { + setError('Password must be at least 6 characters'); + return; + } + + setLoading(true); + + try { + await register({ username, email, password }); + navigate('/networks'); + } catch (err: any) { + setError(err.response?.data?.message || 'Registration failed. Please try again.'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+

Register

+ + {error && ( +
+ {error} +
+ )} + +
+
+ + setUsername(e.target.value)} + required + /> +
+ +
+ + setEmail(e.target.value)} + required + /> +
+ +
+ + setPassword(e.target.value)} + required + /> +
+ +
+ + setConfirmPassword(e.target.value)} + required + /> +
+ +
+ + + Login + +
+
+
+
+ ); +}; + +export default Register; diff --git a/frontend/src/components/layout/Header.tsx b/frontend/src/components/layout/Header.tsx index 078cffc..aa88404 100644 --- a/frontend/src/components/layout/Header.tsx +++ b/frontend/src/components/layout/Header.tsx @@ -1,58 +1,58 @@ -import React from 'react'; -import {Link, useNavigate} from 'react-router-dom'; -import {useAuth} from '../../context/AuthContext'; - -const Header: React.FC = () => { - const {user, logout} = useAuth(); - const navigate = useNavigate(); - - const handleLogout = async () => { - try { - await logout(); - navigate('/login'); - } catch (error) { - console.error('Logout failed:', error); - } - }; - - return ( -
-
- - Friendship Network - - - -
-
- ); -}; - -export default Header; \ No newline at end of file +import React from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import { useAuth } from '../../context/AuthContext'; + +const Header: React.FC = () => { + const { user, logout } = useAuth(); + const navigate = useNavigate(); + + const handleLogout = async () => { + try { + await logout(); + navigate('/login'); + } catch (error) { + console.error('Logout failed:', error); + } + }; + + return ( +
+
+ + Friendship Network + + + +
+
+ ); +}; + +export default Header; diff --git a/frontend/src/components/networks/NetworkList.tsx b/frontend/src/components/networks/NetworkList.tsx index 4e5c4e1..6a77115 100644 --- a/frontend/src/components/networks/NetworkList.tsx +++ b/frontend/src/components/networks/NetworkList.tsx @@ -1,274 +1,274 @@ -import React, { useState } from 'react'; -import { useNetworks } from '../../context/NetworkContext'; -import { useAuth } from '../../context/AuthContext'; -import { useNavigate } from 'react-router-dom'; - -const NetworkList: React.FC = () => { - const { networks, loading, error, createNetwork, deleteNetwork } = useNetworks(); - const { user } = useAuth(); - const [showCreateForm, setShowCreateForm] = useState(false); - const [newNetworkName, setNewNetworkName] = useState(''); - const [newNetworkDescription, setNewNetworkDescription] = useState(''); - const [isPublic, setIsPublic] = useState(false); - const [formError, setFormError] = useState(null); - const [createLoading, setCreateLoading] = useState(false); - const navigate = useNavigate(); - - const handleCreateNetwork = async (e: React.FormEvent) => { - e.preventDefault(); - setFormError(null); - - if (!newNetworkName.trim()) { - setFormError('Network name is required'); - return; - } - - setCreateLoading(true); - - try { - const network = await createNetwork({ - name: newNetworkName.trim(), - description: newNetworkDescription.trim() || undefined, - isPublic - }); - - // Reset form - setNewNetworkName(''); - setNewNetworkDescription(''); - setIsPublic(false); - setShowCreateForm(false); - - // Navigate to the new network - navigate(`/networks/${network._id}`); - } catch (err: any) { - setFormError(err.response?.data?.message || 'Failed to create network'); - } finally { - setCreateLoading(false); - } - }; - - const handleDeleteNetwork = async (id: string) => { - if (window.confirm('Are you sure you want to delete this network? This action cannot be undone.')) { - try { - await deleteNetwork(id); - } catch (err: any) { - alert(err.response?.data?.message || 'Failed to delete network'); - } - } - }; - - if (loading) { - return
Loading networks...
; - } - - return ( -
-
-

My Networks

- -
- - {error && ( -
- {error} -
- )} - - {/* Create Network Form */} - {showCreateForm && ( -
-

Create New Network

- - {formError && ( -
- {formError} -
- )} - -
-
- - setNewNetworkName(e.target.value)} - required - /> -
- -
- -