diff --git a/Client/sportify-court/.firebase/hosting.ZGlzdA.cache b/Client/sportify-court/.firebase/hosting.ZGlzdA.cache new file mode 100644 index 0000000..46b4298 --- /dev/null +++ b/Client/sportify-court/.firebase/hosting.ZGlzdA.cache @@ -0,0 +1,7 @@ +vite.svg,1752623480906,699a02e0e68a579f687d364bbbe7633161244f35af068220aee37b1b33dfb3c7 +Sportify-Courts.png,1753306514770,77a139ab083a120726a83030003dac57a951498e11e0376b6cb05efad3c59bac +Sportify-Court.png,1753103371364,bfb3d09ebb09d85b96f006a565019c2cdef15ac25ac1941c56f38186567492e3 +Sportify-Court-removebg-preview.png,1753306359665,bbe07785d291fc509d6dd62a9e3ff8adeb01e153f5d77d14f3a2e4172184774b +index.html,1753987913839,d181778e0ba6154f686aa9e5a455e1610fa85c6cc5e1c3bc2f4461cff054b45e +assets/index-C3LF-bwm.css,1753987913839,113b4aaa60317eda62802248aab1a51dfcaecb245e2e24b069a4e80c8806f337 +assets/index-DiYXdOaV.js,1753987913839,11534f3da52e12fdece89bab7e2e4d7622e7b7ca9e9a5b011edc1690ece37176 diff --git a/Client/sportify-court/.firebaserc b/Client/sportify-court/.firebaserc new file mode 100644 index 0000000..f5d62b9 --- /dev/null +++ b/Client/sportify-court/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "sportifycourts" + } +} diff --git a/Client/sportify-court/.gitignore b/Client/sportify-court/.gitignore new file mode 100644 index 0000000..3b0b403 --- /dev/null +++ b/Client/sportify-court/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +.env \ No newline at end of file diff --git a/Client/sportify-court/README.md b/Client/sportify-court/README.md new file mode 100644 index 0000000..7059a96 --- /dev/null +++ b/Client/sportify-court/README.md @@ -0,0 +1,12 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/Client/sportify-court/eslint.config.js b/Client/sportify-court/eslint.config.js new file mode 100644 index 0000000..cee1e2c --- /dev/null +++ b/Client/sportify-court/eslint.config.js @@ -0,0 +1,29 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{js,jsx}'], + extends: [ + js.configs.recommended, + reactHooks.configs['recommended-latest'], + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + rules: { + 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], + }, + }, +]) diff --git a/Client/sportify-court/firebase.json b/Client/sportify-court/firebase.json new file mode 100644 index 0000000..2c33c29 --- /dev/null +++ b/Client/sportify-court/firebase.json @@ -0,0 +1,16 @@ +{ + "hosting": { + "public": "dist", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } +} diff --git a/Client/sportify-court/index.html b/Client/sportify-court/index.html new file mode 100644 index 0000000..ecf152a --- /dev/null +++ b/Client/sportify-court/index.html @@ -0,0 +1,22 @@ + + + + + + Sportify Court + + + + + + + +
+ + + diff --git a/Client/sportify-court/package-lock.json b/Client/sportify-court/package-lock.json new file mode 100644 index 0000000..5c90ca0 --- /dev/null +++ b/Client/sportify-court/package-lock.json @@ -0,0 +1,4095 @@ +{ + "name": "sportify-court", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sportify-court", + "version": "0.0.0", + "dependencies": { + "@headlessui/react": "^2.2.5", + "@reduxjs/toolkit": "^2.8.2", + "@tailwindcss/vite": "^4.1.11", + "axios": "^1.11.0", + "dayjs": "^1.11.13", + "framer-motion": "^12.23.9", + "lucide-react": "^0.525.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-redux": "^9.2.0", + "react-router": "^7.7.0", + "sweetalert2": "^11.22.2", + "tailwindcss": "^4.1.11" + }, + "devDependencies": { + "@eslint/js": "^9.30.1", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.6.0", + "eslint": "^9.30.1", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "vite": "^7.0.4" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "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.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", + "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", + "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz", + "integrity": "sha512-wNB5ooIKHQc+Kui96jE/n69rHFWAVoxn5CAzL1Xdd8FG03cgY3MLO+GF9U3W737fYDSgPWA6MReKhBQBop6Pcw==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.2.tgz", + "integrity": "sha512-7cfaOQuCS27HD7DX+6ib2OrnW+b4ZBwDNnCcT0uTyidcmyWb03FnQqJybDBoCnpdxwBSfA94UAYlRCt7mV+TbA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.2", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.28", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.4.tgz", + "integrity": "sha512-JbbpPhp38UmXDDAu60RJmbeme37Jbgsm7NrHGgzYYFKmblzRUh6Pa641dII6LsjwF4XlScDrde2UAzDo/b9KPw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.2" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@headlessui/react": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.5.tgz", + "integrity": "sha512-h1+2Vu1yR5pp/fBcTnwVEW8Kb94Hbxp7MXZLORfDzvSrbmGgiTyaTZ4LI/tPNZnK8eDrYD9s9cMbjm5HS5otIQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.20.2", + "@react-aria/interactions": "^3.25.0", + "@tanstack/react-virtual": "^3.13.9", + "use-sync-external-store": "^1.5.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@react-aria/focus": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.0.tgz", + "integrity": "sha512-7NEGtTPsBy52EZ/ToVKCu0HSelE3kq9qeis+2eEq90XSuJOMaDHUQrA7RC2Y89tlEwQB31bud/kKRi9Qme1dkA==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/interactions": "^3.25.4", + "@react-aria/utils": "^3.30.0", + "@react-types/shared": "^3.31.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.25.4", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.4.tgz", + "integrity": "sha512-HBQMxgUPHrW8V63u9uGgBymkMfj6vdWbB0GgUJY49K9mBKMsypcHeWkWM6+bF7kxRO728/IK8bWDV6whDbqjHg==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-aria/utils": "^3.30.0", + "@react-stately/flags": "^3.1.2", + "@react-types/shared": "^3.31.0", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz", + "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.30.0.tgz", + "integrity": "sha512-ydA6y5G1+gbem3Va2nczj/0G0W7/jUVo/cbN10WA5IizzWIwMP5qhFr7macgbKfHMkZ+YZC3oXnt2NNre5odKw==", + "license": "Apache-2.0", + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-stately/flags": "^3.1.2", + "@react-stately/utils": "^3.10.8", + "@react-types/shared": "^3.31.0", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/flags": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz", + "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.8.tgz", + "integrity": "sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.31.0", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.31.0.tgz", + "integrity": "sha512-ua5U6V66gDcbLZe4P2QeyNgPp4YWD1ymGA6j3n+s8CGExtrCPe64v+g4mvpT8Bnb985R96e4zFT61+m0YCwqMg==", + "license": "Apache-2.0", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.2.tgz", + "integrity": "sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^10.0.3", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", + "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", + "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", + "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", + "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", + "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", + "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", + "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", + "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", + "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", + "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", + "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", + "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", + "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", + "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", + "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", + "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", + "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", + "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", + "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", + "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-x64": "4.1.11", + "@tailwindcss/oxide-freebsd-x64": "4.1.11", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.11", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.11.tgz", + "integrity": "sha512-RHYhrR3hku0MJFRV+fN2gNbDNEh3dwKvY8XJvTxCSXeMOsCRSr+uKvDWQcbizrHgjML6ZmTE5OwMrl5wKcujCw==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", + "tailwindcss": "4.1.11" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz", + "integrity": "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.12", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz", + "integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "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/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/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/react": { + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@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 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/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.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "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.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/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==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "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.190", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.190.tgz", + "integrity": "sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" + } + }, + "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/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", + "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.31.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/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.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/framer-motion": { + "version": "12.23.9", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.9.tgz", + "integrity": "sha512-TqEHXj8LWfQSKqfdr5Y4mYltYLw96deu6/K9kGDd+ysqRJPNwF9nb5mZcrLmybHbU7gcJ+HQar41U3UTGanbbQ==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.9", + "motion-utils": "^12.23.6", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "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/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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/jiti": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.5.1.tgz", + "integrity": "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/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-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/lucide-react": { + "version": "0.525.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.525.0.tgz", + "integrity": "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/motion-dom": { + "version": "12.23.9", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.9.tgz", + "integrity": "sha512-6Sv++iWS8XMFCgU1qwKj9l4xuC47Hp4+2jvPfyTXkqDg2tTzSgX6nWKD4kNFXk0k7llO59LZTPuJigza4A2K1A==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "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==", + "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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/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-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "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.7.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.0.tgz", + "integrity": "sha512-3FUYSwlvB/5wRJVTL/aavqHmfUKe0+Xm9MllkYgGo9eDwNdkvwlJGjpPxono1kCycLt6AnDTgjmXvK3/B4QGuw==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", + "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.45.1", + "@rollup/rollup-android-arm64": "4.45.1", + "@rollup/rollup-darwin-arm64": "4.45.1", + "@rollup/rollup-darwin-x64": "4.45.1", + "@rollup/rollup-freebsd-arm64": "4.45.1", + "@rollup/rollup-freebsd-x64": "4.45.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", + "@rollup/rollup-linux-arm-musleabihf": "4.45.1", + "@rollup/rollup-linux-arm64-gnu": "4.45.1", + "@rollup/rollup-linux-arm64-musl": "4.45.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-musl": "4.45.1", + "@rollup/rollup-linux-s390x-gnu": "4.45.1", + "@rollup/rollup-linux-x64-gnu": "4.45.1", + "@rollup/rollup-linux-x64-musl": "4.45.1", + "@rollup/rollup-win32-arm64-msvc": "4.45.1", + "@rollup/rollup-win32-ia32-msvc": "4.45.1", + "@rollup/rollup-win32-x64-msvc": "4.45.1", + "fsevents": "~2.3.2" + } + }, + "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/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/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/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-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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sweetalert2": { + "version": "11.22.2", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.22.2.tgz", + "integrity": "sha512-GFQGzw8ZXF23PO79WMAYXLl4zYmLiaKqYJwcp5eBF07wiI5BYPbZtKi2pcvVmfUQK+FqL1risJAMxugcPbGIyg==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/limonte" + } + }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vite": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz", + "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.2", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/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/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/Client/sportify-court/package.json b/Client/sportify-court/package.json new file mode 100644 index 0000000..e7792a5 --- /dev/null +++ b/Client/sportify-court/package.json @@ -0,0 +1,38 @@ +{ + "name": "sportify-court", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@headlessui/react": "^2.2.5", + "@reduxjs/toolkit": "^2.8.2", + "@tailwindcss/vite": "^4.1.11", + "axios": "^1.11.0", + "dayjs": "^1.11.13", + "framer-motion": "^12.23.9", + "lucide-react": "^0.525.0", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-redux": "^9.2.0", + "react-router": "^7.7.0", + "sweetalert2": "^11.22.2", + "tailwindcss": "^4.1.11" + }, + "devDependencies": { + "@eslint/js": "^9.30.1", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "@vitejs/plugin-react": "^4.6.0", + "eslint": "^9.30.1", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^16.3.0", + "vite": "^7.0.4" + } +} diff --git a/Client/sportify-court/public/Sportify-Court-removebg-preview.png b/Client/sportify-court/public/Sportify-Court-removebg-preview.png new file mode 100644 index 0000000..a680c51 Binary files /dev/null and b/Client/sportify-court/public/Sportify-Court-removebg-preview.png differ diff --git a/Client/sportify-court/public/Sportify-Court.png b/Client/sportify-court/public/Sportify-Court.png new file mode 100644 index 0000000..25af56f Binary files /dev/null and b/Client/sportify-court/public/Sportify-Court.png differ diff --git a/Client/sportify-court/public/Sportify-Courts.png b/Client/sportify-court/public/Sportify-Courts.png new file mode 100644 index 0000000..d211f04 Binary files /dev/null and b/Client/sportify-court/public/Sportify-Courts.png differ diff --git a/Client/sportify-court/public/vite.svg b/Client/sportify-court/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/Client/sportify-court/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Client/sportify-court/src/App.css b/Client/sportify-court/src/App.css new file mode 100644 index 0000000..f1d8c73 --- /dev/null +++ b/Client/sportify-court/src/App.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/Client/sportify-court/src/App.jsx b/Client/sportify-court/src/App.jsx new file mode 100644 index 0000000..04214cc --- /dev/null +++ b/Client/sportify-court/src/App.jsx @@ -0,0 +1,49 @@ +import { BrowserRouter, Routes, Route, Outlet, Navigate } from "react-router"; +import HomePage from "./pages/HomePage"; +import "./App.css"; +import Register from "./pages/Register"; +import Login from "./pages/Login"; +import PublicCourts from "./pages/PublicCourts"; +import AuthenticatedLayout from "./layout/AuthenLayout"; +import MyBookings from "./pages/MyBooking"; +import PublicCourtDetail from "./pages/CourtDetail"; +import BookingForm from "./pages/CreateBooking"; +import AllBookings from "./pages/AllBookings"; +import ProtectedRoute from "./components/ProtectedRoutes"; +import { store } from "./store"; +import { Provider } from "react-redux"; + +function App() { + // const count = useSelector((state) => state.counter.value); + // const dispatch = useDispatch(); + return ( + + + + } /> + {/* Public */} + } /> + } /> + } /> + } /> + }> + {/* Protected Layout */} + {/* Hanya untuk user & admin */} + } + > + } /> + } /> + + {/* Hanya untuk admin */} + }> + } /> + + + + + + ); +} + +export default App; diff --git a/Client/sportify-court/src/assets/react.svg b/Client/sportify-court/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/Client/sportify-court/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Client/sportify-court/src/components/BookingForm.jsx b/Client/sportify-court/src/components/BookingForm.jsx new file mode 100644 index 0000000..54082e7 --- /dev/null +++ b/Client/sportify-court/src/components/BookingForm.jsx @@ -0,0 +1,174 @@ +// src/components/BookingForm.jsx +import { useState } from "react"; +// import axios from "axios"; +import { api } from "../helpers/http-client"; +import { ErrorAlert, SuccessAlert } from "../helpers/alert"; + +export default function BookingForm({ courtId }) { + const [form, setForm] = useState({ + date: "", + startTime: "", + endTime: "", + }); + const [preference, setPreference] = useState(""); + const [recommendation, setRecommendation] = useState(""); + const [loading, setLoading] = useState(false); + + function handleChange(e) { + setForm({ ...form, [e.target.name]: e.target.value }); + } + + async function handleSubmit(e) { + e.preventDefault(); + try { + const token = localStorage.getItem("access_token"); + await api.post( + "/bookings", + { + CourtId: courtId, + date: form.date, + startTime: form.startTime, + endTime: form.endTime, + }, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + SuccessAlert("Bookings created successfully!"); + } catch (err) { + console.error(err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Create Booking Failed"); + } + } + + async function handleRecommendation() { + setLoading(true); + console.log("🧠 AI Recommendation: Memulai permintaan ke backend..."); + try { + const response = await api.post("/ai/recommend", { + courtId, + preference, + availableSlots: [ + "2025-07-25 07:00 - 08:00", + "2025-07-25 16:00 - 17:00", + "2025-07-26 09:00 - 10:00", + ], + }); + console.log("📦 Rekomendasi dari AI:", response.data); + console.log("📩 Text rekomendasi:", response.data.recommendation); + // setRecommendation("Saran: Booking jam 7 pagi atau jam 4 sore."); + setRecommendation(response.data.recommendation || "Tidak ada hasil."); + } catch (err) { + console.error(err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "AI Recommendation Failed"); + } finally { + setLoading(false); + } + } + + return ( +
+

+ Booking Lapangan +

+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ + + +
+ + {/* AI Recommendation Section */} +
+ + setPreference(e.target.value)} + placeholder="Contoh: pagi hari, cuaca cerah" + className="form-input w-full border border-gray-300 rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-green-500" + /> + + + {recommendation && ( +
+ Rekomendasi: {recommendation} +
+ )} +
+ {loading && ( +

+ Sedang memproses AI... +

+ )} +

+ Debug: {recommendation} +

+
+ ); +} diff --git a/Client/sportify-court/src/components/CancelModal.jsx b/Client/sportify-court/src/components/CancelModal.jsx new file mode 100644 index 0000000..66c6dd2 --- /dev/null +++ b/Client/sportify-court/src/components/CancelModal.jsx @@ -0,0 +1,43 @@ +import { Dialog } from "@headlessui/react"; + +export default function CancelModal({ isOpen, onClose, onConfirm }) { + return ( + + + ); +} diff --git a/Client/sportify-court/src/components/CourtCard.jsx b/Client/sportify-court/src/components/CourtCard.jsx new file mode 100644 index 0000000..1c89e6a --- /dev/null +++ b/Client/sportify-court/src/components/CourtCard.jsx @@ -0,0 +1,51 @@ +import { Link } from "react-router"; + +export default function CourtCard({ court }) { + return ( + +
+ {court.name} + (e.target.src = "https://placehold.co/300x200?text=No+Image") + } + className="w-full h-52 object-cover group-hover:scale-105 transition-transform duration-300 rounded-t-2xl border-b border-gray-200 shadow-sm hover:shadow-md hover:rounded-b-lg hover:border-blue-300 hover:shadow-blue-300/50 transition-all duration-300 rounded-t-2xl shadow-lg hover:shadow-xl hover:border-blue-400 hover:scale-105" + /> +
+

+ {court.name} +

+

+ + {/* Location icon changed to a map pin */} + + + + + {court.location} +

+
+ + Rp {court.pricePerHour?.toLocaleString() ?? "N/A"}{" "} + / jam + + + Lihat Detail + +
+
+
+ + ); +} diff --git a/Client/sportify-court/src/components/EditBookingModal.jsx b/Client/sportify-court/src/components/EditBookingModal.jsx new file mode 100644 index 0000000..9f01ecd --- /dev/null +++ b/Client/sportify-court/src/components/EditBookingModal.jsx @@ -0,0 +1,143 @@ +// src/components/EditBookingModal.jsx +import React, { useState, useEffect } from "react"; + +export default function EditBookingModal({ booking, onClose, onSubmit }) { + const [form, setForm] = useState({ + date: "", + timeStart: "", + timeEnd: "", + }); + + useEffect(() => { + if (booking) { + setForm({ + date: booking.date, + timeStart: booking.timeStart, + timeEnd: booking.timeEnd, + }); + } + }, [booking]); + + const handleChange = (e) => { + setForm({ + ...form, + [e.target.name]: e.target.value, + }); + }; + + const handleSubmit = (e) => { + e.preventDefault(); + onSubmit(form); + }; + + return ( +
+
+ +

+ + + + + + + Edit Booking +

+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ ); +} diff --git a/Client/sportify-court/src/components/Navbar.jsx b/Client/sportify-court/src/components/Navbar.jsx new file mode 100644 index 0000000..fa63dbd --- /dev/null +++ b/Client/sportify-court/src/components/Navbar.jsx @@ -0,0 +1,126 @@ +// src/components/Navbar.jsx +import { useState, useEffect } from "react"; +import { useNavigate } from "react-router"; +import { Menu, X } from "lucide-react"; +import { SuccessAlert } from "../helpers/alert"; +// import { api } from "../helpers/http-client"; + +export default function Navbar() { + const [isOpen, setIsOpen] = useState(false); + const [isLoggedIn, setIsLoggedIn] = useState(false); + const [role, setRole] = useState(null); + const [name, setName] = useState(""); + const navigate = useNavigate(); + + useEffect(() => { + const token = localStorage.getItem("access_token"); + const userRole = localStorage.getItem("role"); // pastikan role disimpan di localStorage saat login + const name = localStorage.getItem("name"); // ambil nama user dari localStorage + setIsLoggedIn(!!token); + setRole(userRole); + setName(name); + }, []); + + const handleLogout = () => { + localStorage.removeItem("access_token"); + localStorage.removeItem("role"); + localStorage.removeItem("name"); + setIsLoggedIn(false); + setRole(null); + setName(""); + SuccessAlert("Logout successful", "You have been logged out successfully."); + navigate("/auth/login"); + }; + + const navItem = (label, path) => ( + navigate(path)} + className="cursor-pointer text-gray-700 hover:text-blue-600" + > + {label} + + ); + + return ( + + ); +} diff --git a/Client/sportify-court/src/components/ProtectedRoutes.jsx b/Client/sportify-court/src/components/ProtectedRoutes.jsx new file mode 100644 index 0000000..164cd39 --- /dev/null +++ b/Client/sportify-court/src/components/ProtectedRoutes.jsx @@ -0,0 +1,19 @@ +// src/components/ProtectedRoute.jsx +import { Navigate, Outlet } from "react-router"; +import { ErrorAlert } from "../helpers/alert"; + +export default function ProtectedRoute({ allowedRoles }) { + const token = localStorage.getItem("access_token"); + const role = localStorage.getItem("role"); // pastikan disimpan saat login + + if (!token) { + ErrorAlert("You must be logged in to access this page", "Unauthorized"); + return ; + } + if (!allowedRoles.includes(role)) { + ErrorAlert("You do not have permission to access this page", "Forbidden"); + return ; + } + + return ; +} diff --git a/Client/sportify-court/src/features/booking/counterSlice.js b/Client/sportify-court/src/features/booking/counterSlice.js new file mode 100644 index 0000000..99459f5 --- /dev/null +++ b/Client/sportify-court/src/features/booking/counterSlice.js @@ -0,0 +1,20 @@ +import { createSlice } from "@reduxjs/toolkit"; + +const initialState = { + value: 0, +}; + +export const courtSlice = createSlice({ + name: "court", + initialState, + reducers: { + setCourts: (state, action) => { + state.courts = action.payload; + }, + }, +}); + +// Action creators are generated for each case reducer function +export const { increment, decrement, incrementByAmount } = courtSlice.actions; + +export default courtSlice.reducer; diff --git a/Client/sportify-court/src/helpers/alert.js b/Client/sportify-court/src/helpers/alert.js new file mode 100644 index 0000000..5b1a0de --- /dev/null +++ b/Client/sportify-court/src/helpers/alert.js @@ -0,0 +1,41 @@ +import Swal from "sweetalert2"; + +export function SuccessAlert(message = "Operation completed successfully.") { + const Toast = Swal.mixin({ + toast: true, + position: "bottom-end", + showConfirmButton: false, + timer: 3000, + timerProgressBar: true, + didOpen: (toast) => { + toast.onmouseenter = Swal.stopTimer; + toast.onmouseleave = Swal.resumeTimer; + }, + }); + Toast.fire({ + icon: "success", + text: message, + }); +} + +export function ErrorAlert( + errorMessage = "An unexpected error occurred. Please try again later.", + title = "Error" +) { + Swal.fire({ + icon: "error", + title, + html: errorMessage, + confirmButtonText: "OK", + confirmButtonColor: "#d32f2f", + background: "#f4f6f8", + color: "#212121", + customClass: { + popup: "swal2-professional-popup", + title: "swal2-professional-title", + htmlContainer: "swal2-professional-html", + confirmButton: "swal2-professional-confirm", + }, + buttonsStyling: true, + }); +} diff --git a/Client/sportify-court/src/helpers/http-client.js b/Client/sportify-court/src/helpers/http-client.js new file mode 100644 index 0000000..c72154b --- /dev/null +++ b/Client/sportify-court/src/helpers/http-client.js @@ -0,0 +1,31 @@ +// src/helpers/http-client.js +import axios from "axios"; + +export const api = axios.create({ + baseURL: "https://api.faizms.com", +}); +// export const api = axios.create({ +// // baseURL: import.meta.env.VITE_API_BASE_URL, +// baseURL: "http://localhost:3000", +// }); + +// export const apiPrivate = axios.create({ +// baseURL: "http://localhost:3000", +// }); + +// Tambahkan token ke setiap request private +api.interceptors.request.use((config) => { + const token = localStorage.getItem("access_token"); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + console.log(">>> Axios Config:", config); + return config; +}); +// apiPrivate.interceptors.request.use((config) => { +// const token = localStorage.getItem("access_token"); +// if (token) { +// config.headers.Authorization = `Bearer ${token}`; +// } +// return config; +// }); diff --git a/Client/sportify-court/src/index.css b/Client/sportify-court/src/index.css new file mode 100644 index 0000000..f1d8c73 --- /dev/null +++ b/Client/sportify-court/src/index.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/Client/sportify-court/src/layout/AuthenLayout.jsx b/Client/sportify-court/src/layout/AuthenLayout.jsx new file mode 100644 index 0000000..6045424 --- /dev/null +++ b/Client/sportify-court/src/layout/AuthenLayout.jsx @@ -0,0 +1,15 @@ +import { Navigate, Outlet } from "react-router"; +import Navbar from "../components/Navbar"; + +export default function AuthenticatedLayout() { + const token = localStorage.getItem("access_token"); + if (!token) { + return ; + } + return ( +
+ + +
+ ); +} diff --git a/Client/sportify-court/src/main.jsx b/Client/sportify-court/src/main.jsx new file mode 100644 index 0000000..dd94577 --- /dev/null +++ b/Client/sportify-court/src/main.jsx @@ -0,0 +1,10 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import "./app.css"; +import App from "./App.jsx"; + +createRoot(document.getElementById("root")).render( + + + +); diff --git a/Client/sportify-court/src/pages/AllBookings.jsx b/Client/sportify-court/src/pages/AllBookings.jsx new file mode 100644 index 0000000..46d4bdf --- /dev/null +++ b/Client/sportify-court/src/pages/AllBookings.jsx @@ -0,0 +1,222 @@ +import { useEffect, useState } from "react"; +import { api } from "../helpers/http-client"; +import EditBookingModal from "../components/EditBookingModal"; +import CancelModal from "../components/CancelModal"; +import { ErrorAlert, SuccessAlert } from "../helpers/alert"; + +export default function AllBookings() { + const [bookings, setBookings] = useState([]); + const [status, setStatus] = useState(""); + const [selectedBooking, setSelectedBooking] = useState(null); + const [showEditModal, setShowEditModal] = useState(false); + const [showCancelModal, setShowCancelModal] = useState(false); + const [selectedBookingId, setSelectedBookingId] = useState(null); + + useEffect(() => { + fetchBookings(); + }, [status]); + + const fetchBookings = async () => { + try { + const token = localStorage.getItem("access_token"); + const query = status ? `?status=${status}` : ""; + const response = await api.get(`/bookings${query}`, { + headers: { Authorization: `Bearer ${token}` }, + }); + console.log("🚀 ~ fetchBookings ~ response.data:", response.data); + setBookings(response.data); + } catch (err) { + console.error("❌ Gagal ambil semua booking:", err); + } + }; + + function handleCancelClick(id) { + setSelectedBookingId(id); + setShowCancelModal(true); + } + async function handleConfirmCancel() { + try { + await api.patch(`/bookings/${selectedBookingId}/status`, { + status: "cancelled", + }); + setShowCancelModal(false); + fetchBookings(); + SuccessAlert("Bookings cancelled successfully!"); + } catch (err) { + console.error("Gagal cancel:", err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Cancel Booking Failed"); + } + } + + async function handleApprove(id) { + try { + await api.patch(`/bookings/${id}/status`, { + status: "approved", + }); + fetchBookings(); + SuccessAlert("Booking approved successfully!"); + } catch (err) { + console.error("Gagal approve:", err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Approve Booking Failed"); + } + } + const openEditModal = (booking) => { + setSelectedBooking(booking); + setShowEditModal(true); + }; + + const handleEditSubmit = async (updatedForm) => { + try { + const token = localStorage.getItem("access_token"); + await api.put(`/bookings/${selectedBooking.id}`, updatedForm, { + headers: { Authorization: `Bearer ${token}` }, + }); + + setShowEditModal(false); + fetchBookings(); + SuccessAlert("Booking updated successfully!"); + } catch (err) { + console.error("Gagal update booking:", err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Update Booking Failed"); + } + }; + + return ( +
+
+

+ All Bookings +

+
+
+ +
+
+ {bookings.length === 0 ? ( +
+ Belum ada booking. +
+ ) : ( +
+ + + + + + + + + + + + + + + {bookings.map((b, i) => ( + + + + + + + + + + + ))} + +
#User + Court + DateTime + Payment + + Status + + Action +
{i + 1}{b.User?.email}{b.Court?.name} + {new Date(b.date).toLocaleDateString("id-ID")} + + {b.timeStart} - {b.timeEnd} + + {b.isPaid ? ( + + Paid + + ) : ( + + Belum Bayar + + )} + + + {b.status.charAt(0).toUpperCase() + b.status.slice(1)} + + +
+ {b.status === "pending" && ( + + )} + {b.status !== "cancelled" && ( + + )} + {b.status === "pending" && ( + + )} +
+
+
+ )} + {showEditModal && selectedBooking && ( + setShowEditModal(false)} + onSubmit={handleEditSubmit} + /> + )} +
+ setShowCancelModal(false)} + onConfirm={handleConfirmCancel} + /> +
+ ); +} diff --git a/Client/sportify-court/src/pages/BookingSuccess.jsx b/Client/sportify-court/src/pages/BookingSuccess.jsx new file mode 100644 index 0000000..df7cd9f --- /dev/null +++ b/Client/sportify-court/src/pages/BookingSuccess.jsx @@ -0,0 +1,22 @@ +import { Link } from "react-router"; + +export default function BookingSuccess() { + return ( +
+
+

+ ✅ Pembayaran Berhasil! +

+

+ Terima kasih telah memesan lapangan di Sportify. +

+ + Lihat Booking Saya + +
+
+ ); +} diff --git a/Client/sportify-court/src/pages/CourtDetail.jsx b/Client/sportify-court/src/pages/CourtDetail.jsx new file mode 100644 index 0000000..1734407 --- /dev/null +++ b/Client/sportify-court/src/pages/CourtDetail.jsx @@ -0,0 +1,61 @@ +// src/pages/PublicCourtDetail.jsx +import { Link, useNavigate, useParams } from "react-router"; +import { useEffect, useState } from "react"; +import { api } from "../helpers/http-client"; +import Navbar from "../components/Navbar"; + +export default function PublicCourtDetail() { + const navigate = useNavigate(); + const { id } = useParams(); + const [court, setCourt] = useState(null); + + useEffect(() => { + async function fetchCourt() { + try { + const res = await api.get(`/public/courts/${id}`); + setCourt(res.data); + } catch (err) { + console.error("❌ Gagal fetch detail:", err); + } + } + fetchCourt(); + }, [id]); + + if (!court) { + return
Loading...
; + } + + return ( +
+ +
+
+ {court.name} + (e.target.src = "https://placehold.co/600x300?text=No+Image") + } + className="w-full h-64 object-cover" + /> +
+

+ {court.name} +

+

{court.location}

+

+ Rp {court.pricePerHour?.toLocaleString()}/jam +

+

{court.description}

+ +
+
+
+
+ ); +} diff --git a/Client/sportify-court/src/pages/CreateBooking.jsx b/Client/sportify-court/src/pages/CreateBooking.jsx new file mode 100644 index 0000000..015384e --- /dev/null +++ b/Client/sportify-court/src/pages/CreateBooking.jsx @@ -0,0 +1,187 @@ +import { useState, useEffect } from "react"; +import { useParams, useNavigate } from "react-router"; +import { api } from "../helpers/http-client"; +import axios from "axios"; +import { ErrorAlert, SuccessAlert } from "../helpers/alert"; + +export default function BookingForm() { + const { id } = useParams(); // court ID + const navigate = useNavigate(); + const [court, setCourt] = useState(null); + const [date, setDate] = useState(""); + const [timeStart, setTimeStart] = useState(""); + const [timeEnd, setTimeEnd] = useState(""); + const [loading, setLoading] = useState(false); + + const [preference, setPreference] = useState(""); + const [recommendation, setRecommendation] = useState(""); + const [loadingAi, setLoadingAi] = useState(false); + + // Ambil detail lapangan berdasarkan ID + useEffect(() => { + async function fetchCourt() { + try { + const res = await api.get(`/public/courts/${id}`); + setCourt(res.data); + SuccessAlert("Court fetched successfully!"); + } catch (err) { + console.error("Gagal fetch court:", err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Fetch Court Failed"); + } + } + fetchCourt(); + }, [id]); + + async function handleRecommendation() { + setLoadingAi(true); + console.log("🧠 AI Recommendation: Memulai permintaan ke backend..."); + try { + const response = await axios.post("http://localhost:3000/ai/recommend", { + courtId: id, + preference, + availableSlots: [ + "2025-07-25 07:00 - 08:00", + "2025-07-25 16:00 - 17:00", + "2025-07-26 09:00 - 10:00", + ], + }); + console.log("📦 Rekomendasi dari AI:", response.data); + console.log("📩 Text rekomendasi:", response.data.recommendation); + // setRecommendation("Saran: Booking jam 7 pagi atau jam 4 sore."); + setRecommendation(response.data.recommendation || "Tidak ada hasil."); + SuccessAlert("AI recommendation fetched successfully!"); + } catch (err) { + console.error(err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "AI Recommendation Failed"); + } finally { + setLoadingAi(false); + } + } + + const handleSubmit = async (e) => { + e.preventDefault(); + try { + setLoading(true); + await api.post("/bookings", { + CourtId: id, + date, + timeStart, + timeEnd, + }); + SuccessAlert("Bookings created successfully!"); + navigate("/bookings/mine"); + } catch (err) { + console.error("❌ Gagal booking:", err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Create Booking Failed"); + } finally { + setLoading(false); + } + }; + + if (!court) return
Loading...
; + + return ( +
+
+

+ Booking: {court.name} +

+
+
+ + setDate(e.target.value)} + className="w-full border border-gray-300 px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 transition" + /> +
+
+
+ + setTimeStart(e.target.value)} + className="w-full border border-gray-300 px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 transition" + /> +
+
+ + setTimeEnd(e.target.value)} + className="w-full border border-gray-300 px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 transition" + /> +
+
+ +
+ + {/* AI Recommendation Section */} +
+ + setPreference(e.target.value)} + placeholder="Contoh: pagi hari, cuaca cerah" + className="w-full border border-gray-300 px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 transition" + /> + + + {recommendation && ( +
+ Rekomendasi AI: {recommendation} +
+ )} +
+ {loadingAi && ( +

+ Sedang memproses AI... +

+ )} +
+
+
+ ); +} diff --git a/Client/sportify-court/src/pages/HomePage.jsx b/Client/sportify-court/src/pages/HomePage.jsx new file mode 100644 index 0000000..26bbafb --- /dev/null +++ b/Client/sportify-court/src/pages/HomePage.jsx @@ -0,0 +1,7 @@ +export default function HomePage() { + return ( +
+

Welcome to the Home Page

+
+ ); +} diff --git a/Client/sportify-court/src/pages/Login.jsx b/Client/sportify-court/src/pages/Login.jsx new file mode 100644 index 0000000..ff20b85 --- /dev/null +++ b/Client/sportify-court/src/pages/Login.jsx @@ -0,0 +1,153 @@ +import { useEffect, useState } from "react"; +import { api } from "../helpers/http-client"; +import { Link, useNavigate } from "react-router"; +import Swal from "sweetalert2"; +import { SuccessAlert, ErrorAlert } from "../helpers/alert"; + +export default function Login() { + const navigate = useNavigate(); + + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + const handleSubmit = async (e) => { + e.preventDefault(); + try { + const response = await api.post("/auth/login", { + email, + password, + }); + console.log("🚀 ~ response:", response); + const access_token = response.data?.access_token; + const role = response.data?.user?.role; + const name = response.data?.user?.name; + if (!access_token) { + throw new Error("Token tidak ditemukan. Cek struktur response."); + } + localStorage.setItem("access_token", access_token); + localStorage.setItem("role", role); + localStorage.setItem("name", name); + + SuccessAlert("Login successful!"); + if (role === "admin") { + navigate("/admin/bookings"); + } else { + navigate("/public/courts"); + } + } catch (err) { + console.error("❌ Login Error:", err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Login Failed"); + } + }; + + async function handleCredentialResponse(response) { + console.log("Encoded JWT ID token: " + response.credential); + try { + const res = await api.post("/auth/login/google", { + id_token: response.credential, + }); + console.log("🚀 ~ Google Login Response:", res); + const access_token = res.data?.access_token; + const role = res.data?.user?.role; + const name = res.data?.user?.name; + if (!access_token) { + throw new Error("Token tidak ditemukan. Cek struktur response."); + } + localStorage.setItem("access_token", access_token); + localStorage.setItem("role", role); + localStorage.setItem("name", name); + console.log("🚀 ~ Google Login Role:", role); + const nameUser = localStorage.getItem("name"); + console.log("🚀 ~ handleCredentialResponse ~ name:", nameUser); + // localStorage.setItem("name", response.data?.user?.name); + if (role === "admin") { + navigate("/admin/bookings"); + } else { + navigate("/public/courts"); + } + SuccessAlert("Google Login successful!"); + } catch (err) { + console.error("❌ Google Login Error:", err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Google Login Failed"); + } + } + useEffect(() => { + google.accounts.id.initialize({ + client_id: import.meta.env.VITE_GOOGLE_CLIENT_ID, + callback: handleCredentialResponse, + }); + google.accounts.id.renderButton( + document.getElementById("buttonDiv"), + { theme: "outline", size: "large" } // customization attributes + ); + // google.accounts.id.prompt(); // also display the One Tap dialog + }, []); + + return ( +
+
+ {/* Logo & Title */} +
+ Logo +

Welcome Back

+

Login to your account

+
+ +
+
+ + setEmail(e.target.value)} + required + /> +
+ +
+ + setPassword(e.target.value)} + required + /> +
+ + +
+
+
+
+ +

+ Don’t have an account?{" "} + + Register here + +

+
+
+ ); +} diff --git a/Client/sportify-court/src/pages/MyBooking.jsx b/Client/sportify-court/src/pages/MyBooking.jsx new file mode 100644 index 0000000..7f5e6be --- /dev/null +++ b/Client/sportify-court/src/pages/MyBooking.jsx @@ -0,0 +1,222 @@ +import { useEffect, useState } from "react"; +import { api } from "../helpers/http-client"; +import { ErrorAlert, SuccessAlert } from "../helpers/alert"; + +export default function MyBookings() { + const [bookings, setBookings] = useState([]); + const [loading, setLoading] = useState(false); + // const [payingId, setPayingId] = useState(null); + + useEffect(() => { + const script = document.createElement("script"); + script.src = "https://app.sandbox.midtrans.com/snap/snap.js"; + script.setAttribute( + "data-client-key", + import.meta.env.VITE_MIDTRANS_CLIENT_KEY + ); + script.async = true; + document.body.appendChild(script); + + return () => { + document.body.removeChild(script); + }; + }, []); + useEffect(() => { + async function fetchBookings() { + try { + setLoading(true); + const res = await api.get("/bookings/mine"); + setBookings(res.data); + SuccessAlert("Bookings fetched successfully!"); + } catch (err) { + console.error("❌ Gagal ambil data bookings:", err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Fetch Booking Failed"); + } finally { + setLoading(false); + } + } + + fetchBookings(); + }, []); + + const handlePayment = async (bookingId) => { + // Trigger snap popup. @TODO: Replace TRANSACTION_TOKEN_HERE with your transaction token + try { + // const bookingRes = await api.get("/bookings/mine"); + // const bookingId = bookingRes.data?.[0]?.id; + + if (!bookingId) { + ErrorAlert("No booking found to pay for."); + return; + } + console.log(localStorage.getItem("access_token")); + console.log("TOKEN DARI LOCAL:", localStorage.getItem("access_token")); + const { data } = await api.post("/payments/midtrans/initiate", { + BookingId: bookingId, + }); + if (window.snap && typeof window.snap.pay === "function") { + window.snap.pay(data.transactionToken, { + onSuccess: async function (result) { + /* You may add your own implementation here */ + console.log("Payment Success:", result); + await api.patch("/payments/me/upgrade", { + orderId: data.orderId, + }); + SuccessAlert("Payment successful!"); + const res = await api.get("/bookings/mine"); + setBookings(res.data); + }, + onError: function (error) { + console.error("Payment Error:", error); + ErrorAlert("Payment failed. Please try again."); + }, + }); + } else { + ErrorAlert("Snap library not loaded. Please check your integration."); + } + } catch (err) { + console.error("Upgrade Error:", err.response || err.message); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Upgrade Failed"); + } + }; + + function formatDate(dateStr) { + return new Date(dateStr).toLocaleDateString("id-ID", { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + }); + } + function formatTime(timeStr) { + return timeStr.slice(0, 5); // contoh: "07:00:00" → "07:00" + } + + return ( +
+

+ + 📋 + {" "} + My Booking +

+ + {loading ? ( +
+ {/* ...loading spinner... */} +
+ ) : bookings.length === 0 ? ( + <> +
+ 📅 +

Belum ada booking.

+
+ + ) : ( +
+ {bookings.map((b) => ( +
+
+
+

+ {b.Court?.name ?? "Unknown Court"} +

+

+ 📍 + {b.Court?.location ?? "Unknown Location"} +

+
+

+ Tanggal:{" "} + {b.date ? formatDate(b.date) : "Unknown Date"} +

+

+ Waktu:{" "} + {b.timeStart ? formatTime(b.timeStart) : "?"} -{" "} + {b.timeEnd ? formatTime(b.timeEnd) : "?"} +

+
+
+ {/* Status Booking dan Pembayaran diletakkan dalam satu container agar tidak terlalu jauh */} +
+
+ + Status Booking: + + + {b.status + ? b.status.charAt(0).toUpperCase() + b.status.slice(1) + : "Unknown"} + + + Pembayaran: + +
+ {b.Payment?.status === "paid" ? ( + + {/* ...icon... */} + Sudah Dibayar + + ) : ( + + {/* ...icon... */} + Belum Bayar + + )} +
+
+
+
+
+ {b.Court?.imageUrl && ( + {b.Court?.name + )} + {b.Payment?.status !== "paid" && + (b.status === "confirmed" || b.status === "approved") && ( + <> + +
+ Silakan klik tombol{" "} + + Bayar Sekarang + {" "} + untuk menyelesaikan pembayaran melalui Midtrans. +
+ + )} +
+
+ ))} +
+ )} +
+ ); +} diff --git a/Client/sportify-court/src/pages/PublicCourts.jsx b/Client/sportify-court/src/pages/PublicCourts.jsx new file mode 100644 index 0000000..e098baa --- /dev/null +++ b/Client/sportify-court/src/pages/PublicCourts.jsx @@ -0,0 +1,177 @@ +// src/pages/PublicCourts.jsx +import { useEffect, useState } from "react"; +import { api } from "../helpers/http-client"; +import { Link } from "react-router"; +import CourtCard from "../components/CourtCard"; +import Navbar from "../components/Navbar"; +// import { useSelector, } from "react-redux"; +// import App from "./App.jsx"; + +export default function PublicCourts() { + // const { courts } = useSelector((state) => state.court); + const [courts, setCourts] = useState([]); + const [pagination, setPagination] = useState({}); + const [search, setSearch] = useState(""); + const [category, setCategory] = useState(""); + const [page, setPage] = useState(1); + + const fetchCourts = async () => { + try { + const response = await api.get("/public/courts", { + params: { + search, + category, + page, + limit: 6, // tampilkan 6 per halaman + }, + }); + setCourts(response.data.data); + setPagination(response.data.pagination); + } catch (err) { + console.error("❌ Gagal fetch courts:", err); + } + }; + + useEffect(() => { + fetchCourts(); + }, [page, search, category]); + + const handleSearch = () => { + setPage(1); + fetchCourts(); + }; + + const categories = [ + "Futsal", + "Mini Soccer", + "Badminton", + "Basket", + "Volley", + "Tennis", + ]; + + return ( +
+ +
+

+ 🏟️ Daftar Lapangan +

+ + {/* Search & Filter */} +
+
+ + setSearch(e.target.value)} + /> +
+
+ +
+
+ + +
+
+
+ {courts.map((court) => ( + + ))} +
+
+ + + {Array.from({ length: pagination.totalPages || 1 }, (_, i) => i + 1) + .filter( + (num) => + num === 1 || + num === (pagination.totalPages || 1) || + (num >= page - 1 && num <= page + 1) + ) + .map((num, idx, arr) => { + if (idx > 0 && num !== arr[idx - 1] + 1) { + return ( + + ... + + ); + } + return ( + + ); + })} + + +
+
+
+ ); +} diff --git a/Client/sportify-court/src/pages/Register.jsx b/Client/sportify-court/src/pages/Register.jsx new file mode 100644 index 0000000..256701a --- /dev/null +++ b/Client/sportify-court/src/pages/Register.jsx @@ -0,0 +1,108 @@ +import { useState } from "react"; +import { api } from "../helpers/http-client"; +import { Link, useNavigate } from "react-router"; +import { ErrorAlert, SuccessAlert } from "../helpers/alert"; + +export default function Register() { + const navigate = useNavigate(); + + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + const handleSubmit = async (e) => { + e.preventDefault(); + try { + console.log("Register Payload:", { name, email, password }); + await api.post("auth/register", { + name, + email, + password, + }); + SuccessAlert("Registration successful!"); + navigate("/auth/login"); + } catch (err) { + console.error("Register Error:", err); + const errors = + err.response?.data?.message || err.message || "Something went wrong!"; + ErrorAlert(errors, "Register Failed"); + } + }; + + return ( +
+
+ {/* Logo & Title */} +
+ Logo +

Create Account

+

+ Create your account to start booking courts +

+
+ +
+
+ + setName(e.target.value)} + required + /> +
+ +
+ + setEmail(e.target.value)} + required + /> +
+ +
+ + setPassword(e.target.value)} + required + /> +
+ + +
+ +

+ Already have an account?{" "} + + Login here + +

+
+
+ ); +} diff --git a/Client/sportify-court/src/store.js b/Client/sportify-court/src/store.js new file mode 100644 index 0000000..d3ed913 --- /dev/null +++ b/Client/sportify-court/src/store.js @@ -0,0 +1,8 @@ +import { configureStore } from "@reduxjs/toolkit"; +import courtReducer from "./features/booking/counterSlice"; + +export const store = configureStore({ + reducer: { + court: courtReducer, + }, +}); diff --git a/Client/sportify-court/vite.config.js b/Client/sportify-court/vite.config.js new file mode 100644 index 0000000..e1b9d0a --- /dev/null +++ b/Client/sportify-court/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss()], + server: { + port: 5174, + }, +}); diff --git a/README.md b/README.md index 1381406..a988ac5 100644 --- a/README.md +++ b/README.md @@ -1 +1,318 @@ -# IP-RMT63 \ No newline at end of file +### IP-RMT63 + +# Sportify Court + +**Sportify Court** adalah aplikasi web untuk booking lapangan olahraga (futsal, badminton, basket, dll) secara online. Fitur utama meliputi pencarian lapangan, booking jadwal, pembayaran digital, rekomendasi AI, dan dashboard admin. Stack teknologi: Express.js, React + Redux, PostgreSQL, Midtrans, GeminiAI. + +**Alasan:** +Aplikasi ini fokus pada **booking lapangan olahraga** seperti futsal, badminton, dan basket. Pengguna dapat memilih waktu, lokasi, dan jenis olahraga yang diinginkan. Fungsionalitas utama adalah menyewa fasilitas olahraga secara efisien berbasis web sehingga termasuk kategori **Sport**. + +## Fitur Utama + +### User + +- Login & Register, Sign in Google +- Lihat dan filter daftar lapangan (dengan kategori & lokasi/nama) +- Booking lapangan berdasarkan tanggal & jam +- Pembayaran digital melalui Midtrans +- Rekomendasi lapangan & jadwal dengan AI (Gemini) +- Riwayat booking + +### Admin + +- Kelola data lapangan (CRUD) + +## 🧩 Struktur Database & ERD Detail + +### 1. Users + +- id (PK) +- name +- email (unique) +- password +- role ("user" | "admin") +- createdAt +- updatedAt + +### 2. Courts + +- id (PK) +- name +- location +- pricePerHour +- description +- imageUrl +- category +- createdAt +- updatedAt + +### 3. Bookings + +- id (PK) +- userId (FK → Users) +- courtId (FK → Courts) +- date +- timeStart +- timeEnd +- status ("pending" | "paid" | "cancelled") +- paymentUrl +- createdAt +- updatedAt + +### 4. Payments + +- id (PK) +- bookingId (FK) +- amount +- method +- status +- paidAt +- createdAt +- updatedAt + +### Relasi Antar Tabel + +- One-to-Many: **Users → Bookings** +- One-to-Many: **Courts → Bookings** +- One-to-One: **Bookings → Payments** + + 🔗 Relasi: + 1 Court → banyak Booking + 1 Court → milik 1 Category + 🔗 Relasi: + 1 Booking → milik 1 User + 1 Booking → milik 1 Court + 🔗 Relasi: + 1 Payment → milik 1 Booking + +```bash +Users 1 ─────< N Bookings +Courts 1 ─────< N Bookings +Bookings 1 ───── 1 Payments +``` + +--- + +## Dokumentasi Endpoint API (RESTful) + +### Auth + +- `POST /register` → Register user baru +- `POST /login` → Login dan generate access token + +### Users + +- `GET /users` → Get semua user (admin only) +- `GET /users/:id` → Detail user by ID +- `DELETE /users/:id` → Hapus user (admin) + +### Categories + +- `GET /categories` → Ambil semua kategori olahraga +- `POST /categories` → Tambah kategori (admin) +- `PUT /categories/:id` → Edit kategori (admin) +- `DELETE /categories/:id` → Hapus kategori (admin) + +### Courts + +- `GET /courts` → Ambil semua lapangan (filter by kategori/area) +- `GET /courts/:id` → Detail lapangan +- `POST /courts` → Tambah lapangan (admin) +- `PUT /courts/:id` → Edit lapangan (admin) +- `DELETE /courts/:id` → Hapus lapangan (admin) + +### Bookings + +- `GET /bookings` → Get semua booking milik user login +- `POST /bookings` → Booking lapangan +- `PUT /bookings/:id/pay` → Update status booking jadi "paid" +- `DELETE /bookings/:id` → Batalkan booking + +### Upload (Bukti Pembayaran) + +- `POST /upload` → Upload bukti transfer (multer) + +### AI Recommendation + +- `POST /recommend` → Kirim prompt ke AI, return rekomendasi lapangan + +### Webhook + +- `POST /payment/webhook` → Callback Midtrans/Stripe untuk update status pembayaran + +# Setup Project + +1. Init project express, install deps + +```bash +npm init -y +npm install express cors dotenv pg sequelize bcryptjs jsonwebtoken +npm install --save-dev sequelize-cli nodemon jest supertest +``` + +2. Init sequelize, (ubah config nya untuk dev dan test environment) -> `postgres` + +```bash +npx sequelize-cli init +touch .gitignore +``` + +a. membuat .env +Isi .env biasanya: - Password database - API key (contoh: OpenAI, Midtrans) - Secret JWT token - Konfigurasi server 3. bikin migration, model + +```bash +npx sequelize-cli model:generate --name User --attributes name:string,email:string,password:string,role:string +npx sequelize-cli model:generate --name Court --attributes name:string,category:string,location:string,pricePerHour:integer,description:text,imageUrl:string +npx sequelize-cli model:generate --name Booking --attributes userId:integer,courtId:integer,date:dateOnly,timeStart:time,timeEnd:time,status:string +npx sequelize-cli model:generate --name Payment --attributes bookingId:integer,amount:integer,method:string,status:string,paymentUrl:string,paidAt:date +``` + +4. setup validation, constraint (optional) + - tambahkan unique dan allowNull false + - model validation +5. setup association + - one to many +6. bikin seeders -> edit file seed + +```bash +npx sequelize-cli seed:generate --name demo-users +npx sequelize-cli seed:generate --name demo-courts +``` + +colom tambahan: + +```bash +npx sequelize migration:generate --name add-isPaid-to-bookings +``` + +7. migrate and seed + +```bash +npx sequelize db:create +npx sequelize db:migrate +npx sequelize db:seed:all +``` + +8. hello world express +9. Setup test -> samakan dengan development (`tambah "_test" di db`) + +- package.json (script) : + "test"-> "jest" + "dev" -> "nodemon bin/www" (export app dari app.js) + +10. testing + +```bash +npx sequelize --env test db:create +npx sequelize --env test db:migrate +``` + +### SETUP + +```bash +npm create vite@latest +cd sportify-court +npm i axios react-router +npm run dev +``` + +```bash +npm install google-auth-library --save +npm install midtrans-client +``` + +### Deploy + +```bash +npm run build (sesuaikan -> sejajar di package.json) +``` + +```bash +npm i -g firebase-tools +firebase login +n +n +login akun google +firebase init hosting +y +Use an existing project +pilih project +dist +y +n +n +firebase deploy +``` + +### RE- DEPLOY + +```bash +npm run build +firebase deploy +``` + +# Setup Project SERVER + +1. Init project express, install deps + +```bash +npm init -y +npm i express sequelize pg bcryptjs jsonwebtoken +npm i -D jest supertest sequelize-cli nodemon +``` + +2. Init sequelize, (ubah config nya untuk dev dan test environment) -> `postgres` + +```bash +npx sequelize init +touch .gitignore +``` + +3. bikin migration, model + +```bash +npx sequelize model:create --name User --attributes email:string,password:string +npx sequelize model:create --name Grocery --attributes title:string,price:integer,tag:string,imageUrl:string,UserId:integer +``` + +4. setup validation, constraint (optional) + - tambahkan unique dan allowNull false + - model validation +5. setup association + - one to many +6. bikin seeders -> edit file seed + +```bash +npx sequelize seed:create --name data +``` + +7. migrate and seed + +```bash +npx sequelize db:create +npx sequelize db:migrate +npx sequelize db:seed:all +``` + +8. hello world express +9. Setup test -> samakan dengan development (`tambah "_test" di db`) + +- package.json (script) : + "test"-> "jest" + "dev" -> "nodemon bin/www" (export app dari app.js) + +10. testing + +```bash +npx sequelize --env test db:create +npx sequelize --env test db:migrate +``` + +# SETUP CLIENT + +```bash +npm create vite@latest +cd sportify-court +npm i axios react-router +npm run dev +``` diff --git a/Server/.env.example b/Server/.env.example new file mode 100644 index 0000000..ea7132c --- /dev/null +++ b/Server/.env.example @@ -0,0 +1,9 @@ +// .env.example +DB_USER=your_postgres_username +DB_PASS=your_postgres_password +DB_NAME=sportify_dev +DB_NAME_TEST=sportify_test +DB_NAME_PROD=sportify_prod +DB_HOST=localhost +PORT=3000 +JWT_SECRET=your_jwt_secret diff --git a/Server/.gitignore b/Server/.gitignore new file mode 100644 index 0000000..3c53f77 --- /dev/null +++ b/Server/.gitignore @@ -0,0 +1,4 @@ +node_modules +.env +.env.test +coverage \ No newline at end of file diff --git a/Server/__tests__/ai.test.js b/Server/__tests__/ai.test.js new file mode 100644 index 0000000..f78cf2f --- /dev/null +++ b/Server/__tests__/ai.test.js @@ -0,0 +1,52 @@ +// __tests__/recommend.test.js atau tests/recommend.test.js +const request = require("supertest"); +const express = require("express"); +const recommendRoute = require("../routes/ai"); + +// 🧪 Mock askGemini +jest.mock("../helpers/AskGemini", () => ({ + askGemini: jest.fn(), +})); + +const { askGemini } = require("../helpers/AskGemini"); + +// 🔧 Setup express app for testing +const app = express(); +app.use(express.json()); +app.use("/", recommendRoute); + +describe("POST /recommend", () => { + it("should return recommendation from Gemini", async () => { + askGemini.mockResolvedValueOnce("Rekomendasi terbaik: 18:00 - 19:00"); + + const res = await request(app) + .post("/recommend") + .send({ + availableSlots: ["17:00 - 18:00", "18:00 - 19:00", "20:00 - 21:00"], + preference: "lebih suka sore hari setelah jam kerja", + }); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty( + "recommendation", + "Rekomendasi terbaik: 18:00 - 19:00" + ); + expect(askGemini).toHaveBeenCalledWith( + expect.stringContaining("lebih suka sore hari setelah jam kerja") + ); + }); + + it("should return 500 if askGemini throws error", async () => { + askGemini.mockRejectedValueOnce(new Error("Gemini Error")); + + const res = await request(app) + .post("/recommend") + .send({ + availableSlots: ["17:00 - 18:00"], + preference: "sore hari", + }); + + expect(res.statusCode).toBe(500); + expect(res.body).toHaveProperty("message"); // tergantung error handler kamu + }); +}); diff --git a/Server/__tests__/askGemini.test.js b/Server/__tests__/askGemini.test.js new file mode 100644 index 0000000..6e56ee0 --- /dev/null +++ b/Server/__tests__/askGemini.test.js @@ -0,0 +1,29 @@ +// Test untuk AskGemini helper secara langsung +const { askGemini } = require("../helpers/AskGemini"); + +// Mock Google Generative AI +jest.mock("@google/generative-ai", () => ({ + GoogleGenerativeAI: jest.fn().mockImplementation(() => ({ + getGenerativeModel: jest.fn().mockReturnValue({ + generateContent: jest.fn().mockResolvedValue({ + response: { + text: jest + .fn() + .mockReturnValue("Mocked Gemini response for unit test"), + }, + }), + }), + })), +})); + +describe("AskGemini Helper Unit Test", () => { + it("should call askGemini function and return response", async () => { + const result = await askGemini("Test prompt for recommendation"); + expect(result).toBe("Mocked Gemini response for unit test"); + }); + + it("should handle different prompts", async () => { + const result = await askGemini("Another test prompt"); + expect(result).toBe("Mocked Gemini response for unit test"); + }); +}); diff --git a/Server/__tests__/auth.test.js b/Server/__tests__/auth.test.js new file mode 100644 index 0000000..64d7d06 --- /dev/null +++ b/Server/__tests__/auth.test.js @@ -0,0 +1,306 @@ +jest.mock("google-auth-library", () => { + return { + OAuth2Client: jest.fn().mockImplementation(() => { + return { + verifyIdToken: jest.fn().mockResolvedValue({ + getPayload: () => ({ + name: "Test User", + email: "test@example.com", + }), + }), + }; + }), + }; +}); +afterEach(() => { + jest.restoreAllMocks(); +}); + +const request = require("supertest"); +const app = require("../app"); +const { sequelize, User } = require("../models"); + +beforeAll(async () => { + await sequelize.sync({ force: true }); + await User.destroy({ where: {} }); // Clear User table to avoid unique constraint errors + await User.create({ + name: "Test User", + email: "test@mail.com", + password: "123456", + role: "user", + }); +}); + +afterAll(async () => { + await sequelize.close(); +}); + +describe("POST /auth/login", () => { + test("should login successfully", async () => { + const res = await request(app).post("/auth/login").send({ + email: "test@mail.com", + password: "123456", + }); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty("access_token"); + }); + test("should return 401 for invalid credentials", async () => { + const res = await request(app).post("/auth/login").send({ + email: "test@mail.com", + password: "wrongpassword", + }); + + expect(res.statusCode).toBe(401); + expect(res.body).toHaveProperty("message", "Invalid email or password"); + }); + test("should return 400 for missing email or password", async () => { + const res = await request(app).post("/auth/login").send({ + email: "", + password: "", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Email is required"); + }); + test("should return 400 for invalid email format", async () => { + const res = await request(app).post("/auth/login").send({ + email: "invalid-email", + password: "123456", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Invalid email format"); + }); + test("should return 404 for non-existing user", async () => { + const res = await request(app).post("/auth/login").send({ + email: "nonexisting@mail.com", + password: "123456", + }); + + expect(res.statusCode).toBe(404); + expect(res.body).toHaveProperty("message", "User not found"); + }); + test("should return 500 for server error", async () => { + const spy = jest.spyOn(User, "findOne").mockImplementation(() => { + throw new Error("Database error"); + }); + + const res = await request(app).post("/auth/login").send({ + email: "test@mail.com", + password: "123456", + }); + + expect(res.statusCode).toBe(500); + expect(res.body).toHaveProperty("message", "Internal Server Error"); + spy.mockRestore(); + }); + test("should return 400 for weak password", async () => { + const res = await request(app).post("/auth/login").send({ + email: "test@mail.com", + password: "123", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty( + "message", + "Password must be at least 6 characters long" + ); + }); + test("should return 400 for missing email", async () => { + const res = await request(app).post("/auth/login").send({ + password: "123456", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Email is required"); + }); + test("should return 400 for missing password", async () => { + const res = await request(app).post("/auth/login").send({ + email: "test@mail.com", + password: "", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Password is required"); + }); + test("should return 400 for empty email", async () => { + const res = await request(app).post("/auth/login").send({ + email: "", + password: "123456", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Email is required"); + }); + test("should return 400 for empty password", async () => { + const res = await request(app).post("/auth/login").send({ + email: "test@mail.com", + password: "", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Password is required"); + }); +}); + +describe("POST /auth/register", () => { + test("should register new user", async () => { + const res = await request(app).post("/auth/register").send({ + name: "Test Register", + email: "reg@mail.com", + password: "123456", + }); + + expect(res.statusCode).toBe(201); + expect(res.body).toHaveProperty("user"); + expect(res.body.user).toHaveProperty("id"); + expect(res.body.user).toHaveProperty("email", "reg@mail.com"); + }); + test("should return 400 for missing fields", async () => { + const res = await request(app).post("/auth/register").send({ + name: "", + email: "", + password: "", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Name cannot be empty"); + }); + test("should return 400 for invalid email format", async () => { + const res = await request(app).post("/auth/register").send({ + name: "Invalid Email", + email: "invalid-email", + password: "123456", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty( + "message", + "Validation isEmail on email failed" + ); + }); + test("should return 400 for weak password", async () => { + const res = await request(app).post("/auth/register").send({ + name: "Weak Password", + email: "weakpassword@mail.com", + password: "123", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty( + "message", + "Password must be at least 6 characters long" + ); + }); + test("should return 500 for server error", async () => { + const spy = jest.spyOn(User, "create").mockImplementation(() => { + throw new Error("Database error"); + }); + + const res = await request(app).post("/auth/register").send({ + name: "Server Error", + email: "servererror@mail.com", + password: "123456", + }); + + expect(res.statusCode).toBe(500); + expect(res.body).toHaveProperty("message", "Internal Server Error"); + spy.mockRestore(); + }); + test("should return 400 for missing email", async () => { + const res = await request(app).post("/auth/register").send({ + name: "Missing Email", + password: "123456", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Email is required"); + }); + test("should return 400 for missing password", async () => { + const res = await request(app).post("/auth/register").send({ + name: "Missing Password", + email: "missingpassword@mail.com", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Password is required"); + }); + test("should return 400 for empty name", async () => { + const res = await request(app).post("/auth/register").send({ + name: "", + email: "emptyname@mail.com", + password: "123456", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Name cannot be empty"); + }); + test("should return 400 for empty email", async () => { + const res = await request(app).post("/auth/register").send({ + name: "Empty Email", + email: "", + password: "123456", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty( + "message", + "Validation isEmail on email failed" + ); + }); + test("should return 400 for empty password", async () => { + const res = await request(app).post("/auth/register").send({ + name: "Empty Password", + email: "emptypassword@mail.com", + password: "", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "Password cannot be empty"); + }); + test("should return 400 for invalid password format", async () => { + const res = await request(app).post("/auth/register").send({ + name: "Invalid Password Format", + email: "invalidpasswordformat@mail.com", + password: "123", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty( + "message", + "Password must be at least 6 characters long" + ); + }); +}); + +describe("POST /auth/login/google", () => { + test("should login with Google successfully", async () => { + const res = await request(app).post("/auth/login/google").send({ + id_token: "valid-google-id-token", + }); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty("access_token"); + }); + test("should return 400 for missing id_token", async () => { + const res = await request(app).post("/auth/login/google").send({}); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty("message", "id_token is required"); + }); + + test("should return 500 for server error", async () => { + const spy = jest.spyOn(User, "findOne").mockImplementation(() => { + throw new Error("Database error"); + }); + + const res = await request(app).post("/auth/login/google").send({ + id_token: "valid-google-id-token", + }); + + expect(res.statusCode).toBe(500); + expect(res.body).toHaveProperty("message", "Internal Server Error"); + spy.mockRestore(); + }); +}); diff --git a/Server/__tests__/booking.test.js b/Server/__tests__/booking.test.js new file mode 100644 index 0000000..f54df07 --- /dev/null +++ b/Server/__tests__/booking.test.js @@ -0,0 +1,220 @@ +const request = require("supertest"); +const app = require("../app"); +const { sequelize, User, Court, Booking } = require("../models"); +const { signToken } = require("../helpers/jwt"); + +let userToken, adminToken, courtId, bookingId; + +beforeAll(async () => { + await sequelize.sync({ force: true }); + + const user = await User.create({ + name: "User Tester", + email: "user@test.com", + password: "123456", + role: "user", + }); + + const admin = await User.create({ + name: "Admin Tester", + email: "admin@test.com", + password: "123456", + role: "admin", + }); + + userToken = signToken({ id: user.id, email: user.email, role: user.role }); + adminToken = signToken({ + id: admin.id, + email: admin.email, + role: admin.role, + }); + + const court = await Court.create({ + name: "Lapangan Basket A", + category: "basket", + location: "Jakarta", + pricePerHour: 100000, + description: "Lapangan basket indoor", + imageUrl: "https://dummyimage.com/basket", + }); + + courtId = court.id; + const booking = await request(app) + .post("/bookings") + .set("Authorization", `Bearer ${userToken}`) + .send({ + CourtId: courtId, + date: "2025-07-26", + timeStart: "09:00", + timeEnd: "11:00", + }); + + bookingId = booking.body.booking.id; +}); + +afterAll(async () => { + await sequelize.close(); +}); + +describe("POST /bookings", () => { + it("should create a booking successfully", async () => { + const res = await request(app) + .post("/bookings") + .set("Authorization", `Bearer ${userToken}`) + .send({ + CourtId: courtId, + date: "2025-07-27", // Different date to avoid overlap + timeStart: "09:00", + timeEnd: "11:00", + }); + + expect(res.statusCode).toBe(201); + expect(res.body).toHaveProperty("message", "Booking created"); + expect(res.body.booking).toHaveProperty("id"); + }); + it("should return 400 if booking time overlaps", async () => { + const res = await request(app) + .post("/bookings") + .set("Authorization", `Bearer ${userToken}`) + .send({ + CourtId: courtId, + date: "2025-07-26", + timeStart: "10:00", + timeEnd: "12:00", + }); + + expect(res.statusCode).toBe(400); + expect(res.body).toHaveProperty( + "message", + "Booking time overlaps with existing booking" + ); + }); + it("should return 404 if court not found", async () => { + const res = await request(app) + .post("/bookings") + .set("Authorization", `Bearer ${userToken}`) + .send({ + CourtId: 9999, // non-existent court + date: "2025-07-26", + timeStart: "09:00", + timeEnd: "11:00", + }); + + expect(res.statusCode).toBe(404); + expect(res.body).toHaveProperty("message", "Court not found"); + }); +}); + +describe("GET /bookings/mine", () => { + it("should return user’s own bookings", async () => { + const res = await request(app) + .get("/bookings/mine") + .set("Authorization", `Bearer ${userToken}`); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body)).toBe(true); + }); + it("should return 401 if not authenticated", async () => { + const res = await request(app).get("/bookings/mine"); + + expect(res.statusCode).toBe(401); + expect(res.body).toHaveProperty("message", "Invalid token"); + }); + it("should return 401 if no token provided", async () => { + const res = await request(app).get("/bookings/mine"); + + expect(res.statusCode).toBe(401); + expect(res.body).toHaveProperty("message", "Invalid token"); + }); +}); + +describe("PATCH /bookings/:id", () => { + let bookingId; + + beforeAll(async () => { + const user = await User.findOne(); + const court = await Court.findOne(); + + const booking = await Booking.create({ + UserId: user.id, + CourtId: court.id, + date: "2025-08-05", + timeStart: "08:00", + timeEnd: "10:00", + }); + bookingId = booking.id; + }); + + it("should update booking and return success", async () => { + const res = await request(app) + .patch(`/bookings/${bookingId}`) + .set("Authorization", `Bearer ${userToken}`) + .send({ + date: "2025-08-06", + timeStart: "09:00", + timeEnd: "11:00", + }); + + expect(res.status).toBe(200); + expect(res.body).toHaveProperty("message", "Booking updated"); + + const updated = await Booking.findByPk(bookingId); + expect(updated.date).toBe("2025-08-06"); + }); +}); + +describe("PATCH /bookings/:id/status", () => { + it("should update booking status to approved", async () => { + const res = await request(app) + .patch(`/bookings/${bookingId}/status`) + .set("Authorization", `Bearer ${adminToken}`) + .send({ + status: "approved", + }); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty("message", "Booking status updated"); + }); + it("should return 403 if user tries to approve booking", async () => { + const res = await request(app) + .patch(`/bookings/${bookingId}/status`) + .set("Authorization", `Bearer ${userToken}`) + .send({ + status: "approved", + }); + expect(res.statusCode).toBe(403); + expect(res.body).toHaveProperty( + "message", + "Only admin can access this resource" + ); + }); + it("should return 404 if booking not found", async () => { + const res = await request(app) + .patch("/bookings/99999/status") + .set("Authorization", `Bearer ${adminToken}`) + .send({ + status: "approved", + }); + + expect(res.statusCode).toBe(404); + expect(res.body).toHaveProperty("message", "Booking not found"); + }); +}); +describe("DELETE /bookings/:id", () => { + it("should delete booking", async () => { + const res = await request(app) + .delete(`/bookings/${bookingId}`) + .set("Authorization", `Bearer ${adminToken}`); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty("message", "Booking deleted"); + }); + it("should return 404 if booking not found", async () => { + const res = await request(app) + .delete("/bookings/99999") + .set("Authorization", `Bearer ${adminToken}`); + + expect(res.statusCode).toBe(404); + expect(res.body).toHaveProperty("message", "Booking not found"); + }); +}); diff --git a/Server/__tests__/court.test.js b/Server/__tests__/court.test.js new file mode 100644 index 0000000..ef7cd75 --- /dev/null +++ b/Server/__tests__/court.test.js @@ -0,0 +1,176 @@ +const request = require("supertest"); +const app = require("../app"); +const { sequelize, Court, User } = require("../models"); +const { signToken } = require("../helpers/jwt"); + +let adminToken; +let courtId; + +beforeAll(async () => { + await sequelize.sync({ force: true }); + + // Buat user admin (jika belum ada) + const admin = await User.create({ + name: "Admin Test", + email: "admin@mail.com", + password: "123456", // pastikan dihash jika pakai hook + role: "admin", + }); + + adminToken = signToken({ id: admin.id, email: admin.email }); + + // Buat data dummy untuk court + const court = await Court.create({ + name: "Lapangan Futsal Senayan", + location: "Jakarta", + pricePerHour: 200000, + category: "Futsal", + description: "Lapangan futsal indoor dengan rumput sintetis", + imageUrl: "https://example.com/futsal.jpg", + }); + + courtId = court.id; +}); + +afterAll(async () => { + await sequelize.close(); +}); + +describe("Court Endpoints", () => { + describe("GET /courts", () => { + it("should return list of courts", async () => { + const res = await request(app) + .get("/courts") + .set("Authorization", `Bearer ${adminToken}`); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body)).toBe(true); + }); + + it("should return courts ordered by ASC", async () => { + const res = await request(app) + .get("/courts?order=ASC") + .set("Authorization", `Bearer ${adminToken}`); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body)).toBe(true); + }); + }); + + describe("GET /courts/:id", () => { + it("should return detail of a court", async () => { + const res = await request(app) + .get(`/courts/${courtId}`) + .set("Authorization", `Bearer ${adminToken}`); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty("id", courtId); + }); + + it("should return 404 if court not found", async () => { + const res = await request(app) + .get("/courts/99999") + .set("Authorization", `Bearer ${adminToken}`); + + expect(res.statusCode).toBe(404); + expect(res.body).toHaveProperty("message", "Court not found"); + }); + }); + + describe("POST /courts", () => { + it("should create a new court", async () => { + const res = await request(app) + .post("/courts") + .set("Authorization", `Bearer ${adminToken}`) + .send({ + name: "Lapangan Basket BSD", + location: "Tangerang", + category: "Futsal", + pricePerHour: 150000, + description: "Lapangan basket outdoor luas", + imageUrl: "https://example.com/basket.jpg", + }); + + expect(res.statusCode).toBe(201); + expect(res.body).toHaveProperty("id"); + }); + + it("should return 400 for invalid court data", async () => { + const res = await request(app) + .post("/courts") + .set("Authorization", `Bearer ${adminToken}`) + .send({ + // Missing required fields + name: "", + location: "", + }); + + expect(res.statusCode).toBe(400); + }); + }); + + describe("PUT /courts/:id", () => { + it("should update court data", async () => { + const res = await request(app) + .put(`/courts/${courtId}`) + .set("Authorization", `Bearer ${adminToken}`) + .send({ + name: "Lapangan Futsal Update", + location: "Jakarta Selatan", + category: "Futsal", + pricePerHour: 250000, + description: "Updated description", + imageUrl: "https://example.com/updated.jpg", + }); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty("message", "Court updated successfully"); + }); + + it("should return 404 if court not found for update", async () => { + const res = await request(app) + .put("/courts/99999") + .set("Authorization", `Bearer ${adminToken}`) + .send({ + name: "Updated Name", + location: "Updated Location", + category: "Futsal", + pricePerHour: 250000, + description: "Updated description", + imageUrl: "https://example.com/updated.jpg", + }); + + expect(res.statusCode).toBe(404); + expect(res.body).toHaveProperty("message", "Court not found"); + }); + }); + + describe("DELETE /courts/:id", () => { + it("should delete court", async () => { + const courtToDelete = await Court.create({ + name: "Lapangan Voli", + location: "Bekasi", + category: "Futsal", + pricePerHour: 100000, + description: "Lapangan voli indoor", + imageUrl: "https://example.com/voli.jpg", + }); + + const res = await request(app) + .delete(`/courts/${courtToDelete.id}`) + .set("Authorization", `Bearer ${adminToken}`); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty("message", "Court deleted successfully"); + }); + + it("should return 404 if court not found for delete", async () => { + const res = await request(app) + .delete("/courts/99999") + .set("Authorization", `Bearer ${adminToken}`); + + expect(res.statusCode).toBe(404); + expect(res.body).toHaveProperty("message", "Court not found"); + }); + }); +}); diff --git a/Server/__tests__/payment.test.js b/Server/__tests__/payment.test.js new file mode 100644 index 0000000..8344632 --- /dev/null +++ b/Server/__tests__/payment.test.js @@ -0,0 +1,110 @@ +process.env.CALLBACK_TOKEN = "test-callback"; +const request = require("supertest"); +const app = require("../app"); // pastikan ini mengarah ke express instance kamu +const { sequelize, User, Court, Booking, Payment } = require("../models"); +const { signToken } = require("../helpers/jwt"); +require("dotenv").config({ path: ".env.test" }); + +jest.mock("midtrans-client", () => { + return { + Snap: jest.fn().mockImplementation(() => ({ + createTransaction: jest.fn().mockResolvedValue({ + token: "dummy-token", + redirect_url: "https://midtrans.com/pay/123", + }), + })), + }; +}); + +jest.mock("axios", () => ({ + get: jest.fn().mockResolvedValue({ + data: { + transaction_status: "capture", + status_code: "200", + status_message: "Success", + }, + }), +})); + +let access_token; +let courtId, bookingId; + +beforeAll(async () => { + await sequelize.sync({ force: true }); + + const user = await User.create({ + name: "Test User", + email: "testuser@mail.com", + password: "123456", // jika pakai hashing, pastikan cocok + }); + + access_token = signToken({ id: user.id, email: user.email, name: user.name }); + + const court = await Court.create({ + name: "Lapangan A", + location: "Jakarta", + pricePerHour: 100000, + category: "Futsal", + description: "Lapangan futsal standar nasional", + imageUrl: "https://via.placeholder.com/300", + }); + courtId = court.id; + + const booking = await Booking.create({ + UserId: user.id, + CourtId: court.id, + date: "2025-07-30", + timeStart: "08:00", + timeEnd: "10:00", + isPaid: true, + }); + bookingId = booking.id; +}); + +afterAll(async () => { + await sequelize.close(); +}); + +describe("Payment Endpoints", () => { + describe("POST /payments/midtrans/initiate", () => { + it("should initiate Midtrans transaction and return token", async () => { + const res = await request(app) + .post("/payments/midtrans/initiate") + .set("Authorization", `Bearer ${access_token}`) + .send({ + BookingId: bookingId, + amount: 100000, + }); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty("transactionToken"); + expect(res.body).toHaveProperty("orderId"); + expect(res.body).toHaveProperty("message", "Order created"); + }); + }); + + describe("PATCH /payments/me/upgrade", () => { + it("should upgrade user account to premium", async () => { + // First create a payment record with a mock orderId + const payment = await Payment.create({ + BookingId: bookingId, + amount: 10000, + orderId: "MOCK-ORDER-123", + status: "pending", + }); + + const res = await request(app) + .patch("/payments/me/upgrade") + .set("Authorization", `Bearer ${access_token}`) + .send({ + orderId: payment.orderId, + }); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty( + "message", + "Account upgraded successfully" + ); + }); + }); +}); diff --git a/Server/__tests__/public.test.js b/Server/__tests__/public.test.js new file mode 100644 index 0000000..11a7ca1 --- /dev/null +++ b/Server/__tests__/public.test.js @@ -0,0 +1,75 @@ +const request = require("supertest"); +const app = require("../app"); +const { sequelize, Court } = require("../models"); + +beforeAll(async () => { + await sequelize.sync({ force: true }); + + await Court.bulkCreate([ + { + name: "Lapangan Futsal A", + category: "futsal", + location: "Jakarta", + pricePerHour: 100000, + description: "Lapangan futsal indoor", + imageUrl: "https://dummyimage.com/futsalA", + }, + { + name: "Lapangan Basket B", + category: "basket", + location: "Bandung", + pricePerHour: 120000, + description: "Lapangan basket outdoor", + imageUrl: "https://dummyimage.com/basketB", + }, + ]); +}); + +afterAll(async () => { + await sequelize.close(); +}); + +describe("Public Endpoints", () => { + describe("GET /public/courts", () => { + it("should return courts with pagination", async () => { + const res = await request(app).get("/public/courts"); + + expect(res.statusCode).toBe(200); + expect(Array.isArray(res.body.data)).toBe(true); + expect(res.body).toHaveProperty("pagination"); + }); + + it("should return courts filtered by category", async () => { + const res = await request(app).get("/public/courts?category=basket"); + + expect(res.statusCode).toBe(200); + expect(res.body.data.length).toBe(1); + expect(res.body.data[0].category).toBe("basket"); + }); + + it("should return courts filtered by search", async () => { + const res = await request(app).get("/public/courts?search=jakarta"); + + expect(res.statusCode).toBe(200); + expect(res.body.data.length).toBe(1); + expect(res.body.data[0].location.toLowerCase()).toContain("jakarta"); + }); + }); + + describe("GET /public/courts/:id", () => { + it("should return detail of a specific court", async () => { + const court = await Court.findOne(); + const res = await request(app).get(`/public/courts/${court.id}`); + + expect(res.statusCode).toBe(200); + expect(res.body).toHaveProperty("name", court.name); + }); + + it("should return 404 if court not found", async () => { + const res = await request(app).get("/public/courts/9999"); + + expect(res.statusCode).toBe(404); + expect(res.body).toHaveProperty("message", "Court not found"); + }); + }); +}); diff --git a/Server/app.js b/Server/app.js new file mode 100644 index 0000000..e6e6518 --- /dev/null +++ b/Server/app.js @@ -0,0 +1,46 @@ +console.log({ env: process.env.NODE_ENV }); +if (process.env.NODE_ENV !== "production") { + // hanya dipake ketika proses development + // kalo production kita tidak menggunakan library dotenv -> env bawaan dari pm2 (runner) + require("dotenv").config(); +} + +const express = require("express"); +const cors = require("cors"); +const app = express(); +const authRoutes = require("./routes/auth"); // nanti kita buat +const publicRoutes = require("./routes/public"); +const courtRoutes = require("./routes/courts"); // nanti +const bookingRoutes = require("./routes/bookings"); // nanti +const paymentRoutes = require("./routes/payments"); // nanti +// const errorHandler = require("./middleware/errorHandler"); // nanti +const aiRoutes = require("./routes/ai"); // untuk AI + +app.use(cors()); +app.use(express.urlencoded({ extended: false })); + +// ⚠️ PENTING: PASANG RAW BODY HANYA UNTUK MIDTRANS CALLBACK +// app.use( +// "/payments/midtrans/callback", +// express.raw({ type: "*/*" }) // Midtrans butuh raw body +// ); +// app.get("/", (req, res) => { +// res.status(200).json({ message: "its my life" }); +// }); + +app.use(express.json()); + +// Routes +app.use("/auth", authRoutes); // POST /auth/register, /login +app.use("/public", publicRoutes); + +app.use("/courts", courtRoutes); +app.use("/bookings", bookingRoutes); +app.use("/payments", paymentRoutes); +app.use("/ai", aiRoutes); +// console.log("✅ /ai route berhasil dimount"); +// router.use(authentication); // Semua route setelah ini butuh login +// Error handler +// app.use(errorHandler); + +module.exports = app; diff --git a/Server/config/config.js b/Server/config/config.js new file mode 100644 index 0000000..d1f12e9 --- /dev/null +++ b/Server/config/config.js @@ -0,0 +1,28 @@ +require("dotenv").config(); + +module.exports = { + development: { + username: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME, + host: process.env.DB_HOST, + dialect: "postgres", + }, + test: { + username: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.DB_NAME_TEST || "sportify_test", + host: process.env.DB_HOST, + dialect: "postgres", + }, + production: { + use_env_variable: "DATABASE_URL", + dialect: "postgres", + dialectOptions: { + ssl: { + require: true, + rejectUnauthorized: false, + }, + }, + }, +}; diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js new file mode 100644 index 0000000..7f19bec --- /dev/null +++ b/Server/controllers/authController.js @@ -0,0 +1,104 @@ +const { comparePassword } = require("../helpers/bcrypt"); +const { signToken } = require("../helpers/jwt"); +const { User } = require("../models"); + +const { OAuth2Client } = require("google-auth-library"); +const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID); + +module.exports = class authController { + static async register(req, res, next) { + try { + const { name, email, password, role } = req.body; + + const newUser = await User.create(req.body); + res.status(201).json({ + message: "User registered successfully", + user: { + id: newUser.id, + name: newUser.name, + email: newUser.email, + role: newUser.role, + }, + }); + } catch (err) { + next(err); + } + } + + static async login(req, res, next) { + const { email, password } = req.body; + try { + if (!email) { + throw { name: "BadRequest", message: "Email is required" }; // 400 + } + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(email)) { + throw { name: "BadRequest", message: "Invalid email format" }; // 400 + } + if (!password) { + throw { name: "BadRequest", message: "Password is required" }; // 400 + } + if (password.length < 6) { + throw { + name: "BadRequest", + message: "Password must be at least 6 characters long", + }; + } + const user = await User.findOne({ where: { email } }); + if (!user) { + return res.status(404).json({ message: "User not found" }); + } + const isValidPassword = comparePassword(password, user.password); + if (!isValidPassword) { + return res.status(401).json({ message: "Invalid email or password" }); + } + const accessToken = signToken({ id: user.id, role: user.role }); + res.status(200).json({ + message: "Login Success", + access_token: accessToken, + user: { + id: user.id, + name: user.name, + role: user.role, + }, + }); + } catch (err) { + next(err); + } + } + + static async googleLogin(req, res, next) { + const { id_token } = req.body; + try { + if (!id_token) { + throw { name: "BadRequest", message: "id_token is required" }; // 400 + } + const ticket = await client.verifyIdToken({ + idToken: id_token, + audience: process.env.GOOGLE_CLIENT_ID, + }); + const { name, email } = ticket.getPayload(); + let user = await User.findOne({ where: { email } }); + if (!user) { + user = await User.create({ + name, + email, + password: Math.random().toString(36).slice(-8), + role: "user", + }); + } + const access_token = signToken({ id: user.id, role: user.role }); + res.status(200).json({ + message: "Google Login Success", + access_token, + user: { + id: user.id, + name: user.name, + role: user.role, + }, + }); + } catch (err) { + next(err); + } + } +}; diff --git a/Server/controllers/bookingController.js b/Server/controllers/bookingController.js new file mode 100644 index 0000000..e63c2cf --- /dev/null +++ b/Server/controllers/bookingController.js @@ -0,0 +1,181 @@ +const { Booking, Court, User, Payment } = require("../models"); + +module.exports = class bookingController { + //* Buat Booking (user) + static async getMyBookings(req, res, next) { + try { + const userId = req.user.id; + // console.log("🚀 ~ getMyBookings ~ userId:", userId); + const bookings = await Booking.findAll({ + where: { UserId: userId }, + include: [ + Court, + { + model: Payment, + attributes: ["status", "paidAt"], + required: false, + }, + ], + }); + res.status(200).json(bookings); + } catch (err) { + // console.log("🚀 ~ getMyBookings ~ err:", err); + next(err); + } + } + //* Ambil semua booking (admin) + static async getAllBookings(req, res, next) { + try { + const { status } = req.query; + + const options = { + include: [ + { model: Court }, + { + model: User, + attributes: ["id", "email", "name"], + }, + ], + order: [["createdAt", "DESC"]], + }; + + // Tambahkan filter jika ada query status + if (status) { + options.where = { status }; + // console.log("🚀 ~ getAllBookings ~ options:", options); + } + + const bookings = await Booking.findAll(options); + res.status(200).json(bookings); + } catch (err) { + next(err); // pastikan error dikirim ke error handler + } + } + //* Create Booking (user) + static async createBooking(req, res, next) { + try { + const userId = req.user.id; + const { CourtId, date, timeStart, timeEnd } = req.body; + + // Check if court exists + const court = await Court.findByPk(CourtId); + if (!court) { + throw { name: "NotFound", message: "Court not found" }; + } + + // Check for overlapping bookings + const existingBooking = await Booking.findOne({ + where: { + CourtId, + date, + status: { [require("sequelize").Op.ne]: "cancelled" }, // exclude cancelled bookings + [require("sequelize").Op.or]: [ + // New booking starts during existing booking + { + timeStart: { [require("sequelize").Op.lte]: timeStart }, + timeEnd: { [require("sequelize").Op.gt]: timeStart }, + }, + // New booking ends during existing booking + { + timeStart: { [require("sequelize").Op.lt]: timeEnd }, + timeEnd: { [require("sequelize").Op.gte]: timeEnd }, + }, + // New booking completely contains existing booking + { + timeStart: { [require("sequelize").Op.gte]: timeStart }, + timeEnd: { [require("sequelize").Op.lte]: timeEnd }, + }, + ], + }, + }); + + if (existingBooking) { + throw { + name: "BadRequest", + message: "Booking time overlaps with existing booking", + }; + } + + const booking = await Booking.create({ + CourtId, + UserId: userId, + date, + timeStart, + timeEnd, + }); + res.status(201).json({ message: "Booking created", booking }); + } catch (err) { + next(err); + } + } + //* Update Booking (user can update their own, admin can update any) + static async updateBooking(req, res, next) { + try { + const { id } = req.params; + const { date, timeStart, timeEnd, status } = req.body; + const booking = await Booking.findByPk(id); + if (!booking) throw { name: "NotFound", message: "Booking not found" }; + + // Check if user owns this booking (unless admin) + if (req.user.role !== "admin" && booking.UserId !== req.user.id) { + throw { + name: "Forbidden", + message: "You can only update your own bookings", + }; + } + + //* Validasi hanya boleh update booking dengan status pending + if (booking.status !== "pending") { + throw { + name: "Forbidden", + message: "Only bookings with status 'pending' can be updated", + }; + } + //* Update hanya field tertentu + await booking.update({ + date: date || booking.date, + timeStart: timeStart || booking.timeStart, + timeEnd: timeEnd || booking.timeEnd, + status: status || booking.status, + }); + + res.status(200).json({ message: "Booking updated" }); + } catch (err) { + next(err); + } + } + //* Update Booking Status (admin) + static async updateBookingStatus(req, res, next) { + try { + const { id } = req.params; + const { status } = req.body; + + const booking = await Booking.findByPk(id); + if (!booking) throw { name: "NotFound", message: "Booking not found" }; + + // Validasi status (opsional, hanya allow certain status) + const validStatuses = ["pending", "approved", "cancelled"]; + if (!validStatuses.includes(status)) { + throw { name: "BadRequest", message: "Invalid status value" }; + } + + await booking.update({ status }); + + res.status(200).json({ message: "Booking status updated", booking }); + } catch (err) { + next(err); + } + } + //* Hapus Booking (admin) + static async deleteBooking(req, res, next) { + try { + const { id } = req.params; + const booking = await Booking.findByPk(id); + if (!booking) throw { name: "NotFound", message: "Booking not found" }; + await booking.destroy(); + res.status(200).json({ message: "Booking deleted" }); + } catch (err) { + next(err); + } + } +}; diff --git a/Server/controllers/courtController.js b/Server/controllers/courtController.js new file mode 100644 index 0000000..0cf9c8b --- /dev/null +++ b/Server/controllers/courtController.js @@ -0,0 +1,84 @@ +const { Court } = require("../models"); + +module.exports = class courtController { + static async getAllCourts(req, res, next) { + try { + const { order = "DESC" } = req.query; + + const courts = await Court.findAll({ + order: [["updatedAt", order.toUpperCase()]], // ASC / DESC + }); + res.status(200).json(courts); + } catch (err) { + next(err); + } + } + + static async getCourtById(req, res, next) { + try { + const courtId = req.params.id; + const court = await Court.findByPk(courtId); + if (!court) { + throw { name: "NotFound", message: "Court not found" }; + } + res.status(200).json(court); + } catch (err) { + next(err); + } + } + + static async createCourt(req, res, next) { + try { + const { name, category, location, pricePerHour, description, imageUrl } = + req.body; + const newCourt = await Court.create({ + name, + category, + location, + pricePerHour, + description, + imageUrl, + }); + res.status(201).json(newCourt); + } catch (err) { + next(err); + } + } + + static async updateCourt(req, res, next) { + try { + const courtId = req.params.id; + const { name, category, location, pricePerHour, description, imageUrl } = + req.body; + const court = await Court.findByPk(courtId); + if (!court) { + throw { name: "NotFound", message: "Court not found" }; + } + await court.update({ + name, + category, + location, + pricePerHour, + description, + imageUrl, + }); + res.status(200).json({ message: "Court updated successfully" }); + } catch (err) { + next(err); + } + } + + static async deleteCourt(req, res, next) { + try { + const courtId = req.params.id; + const court = await Court.findByPk(courtId); + if (!court) { + throw { name: "NotFound", message: "Court not found" }; + } + await court.destroy(); + res.status(200).json({ message: "Court deleted successfully" }); + } catch (err) { + next(err); + } + } +}; diff --git a/Server/controllers/paymentController.js b/Server/controllers/paymentController.js new file mode 100644 index 0000000..37e122c --- /dev/null +++ b/Server/controllers/paymentController.js @@ -0,0 +1,161 @@ +const { Payment, Booking, Court } = require("../models"); +const midtransClient = require("midtrans-client"); +const { signToken } = require("../helpers/jwt"); +const { Op, or } = require("sequelize"); +const dayjs = require("dayjs"); +const axios = require("axios"); + +module.exports = class paymentController { + static async initiateMidtransTrx(req, res, next) { + try { + console.log("Request Headers:", req.headers); + console.log("Request Body:", req.body); + + if (!process.env.MIDTRANS_SERVER_KEY) { + throw { + name: "ServerError", + message: "MIDTRANS_SERVER_KEY is missing", + }; + } + console.log("MIDTRANS_SERVER_KEY:", process.env.MIDTRANS_SERVER_KEY); + let snap = new midtransClient.Snap({ + // Set to true if you want Production Environment (accept real transaction). + isProduction: false, + serverKey: process.env.MIDTRANS_SERVER_KEY, + clientKey: process.env.MIDTRANS_CLIENT_KEY, + }); + console.log("Request Body:", req.body); + const { BookingId } = req.body; + if (!BookingId) { + throw { name: "InvalidInput", message: "BookingId is required" }; + } + console.log("User ID:", req.user.id); + console.log("Booking ID:", req.body.BookingId); + if (!req.body.BookingId) { + return res.status(400).json({ message: "BookingId is required" }); + } + if (!req.user) { + return res + .status(401) + .json({ message: "Unauthorized: user not found" }); + } + // Cari booking berdasarkan BookingId dan UserId + const booking = await Booking.findOne({ + where: { + id: BookingId, + UserId: req.user.id, + }, + include: [{ model: Court }], + }); + + if (!booking) { + return res.status(404).json({ message: "Booking not found" }); + } + + const orderId = `BOOK-${Date.now()}-${Math.floor(Math.random() * 1000)}`; + if (!orderId) { + return res.status(500).json({ message: "Order ID generation failed" }); + } + const amount = booking.Court?.pricePerHour || 10000; + let parameter = { + // ini adalah data detail order + transaction_details: { + order_id: orderId, + gross_amount: amount, + }, + // ini adalah data jenis pembayaran + credit_card: { + secure: true, + }, + // ini adalah data customer + customer_details: { + first_name: req.user.name, + email: req.user.email, + }, + }; + let transaction; + try { + // 1.create transaction to midtrans + transaction = await snap.createTransaction(parameter); + console.log("📦 Midtrans Transaction Response:", transaction); + // transaction token + if (!transaction.token) { + throw new Error( + "Transaction token is missing from Midtrans response" + ); + } + // console.log("transactionToken:", transactionToken); + } catch (err) { + console.error("Midtrans Error:", err.response?.data || err.message); + throw err; + } + let transactionToken = transaction.token; + // 2. create order in our database + + await Payment.create({ + BookingId: booking.id, + amount, + orderId, + }); + + res.json({ message: "Order created", transactionToken, orderId }); + } catch (err) { + console.log("❌ INITIATE MIDTRANS TRX ERROR:"); + console.dir(err, { depth: null }); + next(err); + } + } + + static async upgradeAccount(req, res, next) { + // check orderId, order ke midtrans, apakah sudah dibayar atau belum + try { + const { orderId } = req.body; + + // Cek apakah user sudah pernah upgrade + const order = await Payment.findOne({ + where: { + orderId, + }, + }); + if (!order) { + return res.status(404).json({ message: "Order not found" }); + } + console.log("🔍 Order:", order); + console.log("Headers:", req.headers); + console.log("Authorization Header:", req.headers.authorization); + + if (order.status === "paid") { + return res.status(400).json({ message: "Account already upgraded" }); + } + + const serverKey = process.env.MIDTRANS_SERVER_KEY; + const base64ServerKey = Buffer.from(serverKey + ":").toString("base64"); + console.log(`Authorization: Basic ${base64ServerKey}`); + const { data } = await axios.get( + `https://api.sandbox.midtrans.com/v2/${orderId}/status`, + { + headers: { + Authorization: `Basic ${base64ServerKey}`, + }, + } + ); + console.log("📦 Midtrans Response:", data); + // midtrans validasi status pembayaran + if (data.transaction_status === "capture" && data.status_code === "200") { + // Update status pembayaran di database + await order.update({ + status: "paid", + paidAt: new Date(), + }); + res.status(200).json({ message: "Account upgraded successfully" }); + } else { + return res.status(400).json({ + message: "Payment not completed or invalid transaction status", + midtransMessage: data.status_message, + }); + } + } catch (err) { + next(err); + } + } +}; diff --git a/Server/controllers/publicController.js b/Server/controllers/publicController.js new file mode 100644 index 0000000..0cf2841 --- /dev/null +++ b/Server/controllers/publicController.js @@ -0,0 +1,52 @@ +const { Court } = require("../models"); +const { Op } = require("sequelize"); + +module.exports = class publicController { + static async getCourts(req, res, next) { + try { + const { search = "", category, page = 1, limit = 10 } = req.query; + const where = {}; + // Search by name or location (case-insensitive) + if (search && !category) { + where[Op.or] = [ + { name: { [Op.iLike]: `%${search}%` } }, + { location: { [Op.iLike]: `%${search}%` } }, + ]; + } + // Filter by category + if (category) { + where.category = { [Op.iLike]: `%${category}%` }; + } + // console.log("FILTER CONDITIONS:", { where }); + const offset = (page - 1) * limit; + const { rows, count } = await Court.findAndCountAll({ + where, + limit: +limit, + offset: +offset, + }); + res.status(200).json({ + data: rows, + pagination: { + page: +page, + totalPages: Math.ceil(count / limit), + totalData: count, + }, + }); + } catch (err) { + next(err); + } + } + + static async getCourtsById(req, res, next) { + try { + const courtId = req.params.id; + const court = await Court.findByPk(courtId); + if (!court) { + throw { name: "NotFound", message: "Court not found" }; + } + res.status(200).json(court); + } catch (err) { + next(err); + } + } +}; diff --git a/Server/coverage/clover.xml b/Server/coverage/clover.xml new file mode 100644 index 0000000..2b4f1a4 --- /dev/null +++ b/Server/coverage/clover.xml @@ -0,0 +1,518 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Server/coverage/coverage-final.json b/Server/coverage/coverage-final.json new file mode 100644 index 0000000..0b16f71 --- /dev/null +++ b/Server/coverage/coverage-final.json @@ -0,0 +1,25 @@ +{"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\app.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\app.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":43}},"1":{"start":{"line":2,"column":0},"end":{"line":6,"column":1}},"2":{"start":{"line":5,"column":2},"end":{"line":5,"column":29}},"3":{"start":{"line":8,"column":16},"end":{"line":8,"column":34}},"4":{"start":{"line":9,"column":13},"end":{"line":9,"column":28}},"5":{"start":{"line":10,"column":12},"end":{"line":10,"column":21}},"6":{"start":{"line":11,"column":19},"end":{"line":11,"column":43}},"7":{"start":{"line":12,"column":21},"end":{"line":12,"column":47}},"8":{"start":{"line":13,"column":20},"end":{"line":13,"column":46}},"9":{"start":{"line":14,"column":22},"end":{"line":14,"column":50}},"10":{"start":{"line":15,"column":22},"end":{"line":15,"column":50}},"11":{"start":{"line":17,"column":17},"end":{"line":17,"column":39}},"12":{"start":{"line":19,"column":0},"end":{"line":19,"column":16}},"13":{"start":{"line":20,"column":0},"end":{"line":20,"column":49}},"14":{"start":{"line":31,"column":0},"end":{"line":31,"column":24}},"15":{"start":{"line":34,"column":0},"end":{"line":34,"column":29}},"16":{"start":{"line":35,"column":0},"end":{"line":35,"column":33}},"17":{"start":{"line":37,"column":0},"end":{"line":37,"column":32}},"18":{"start":{"line":38,"column":0},"end":{"line":38,"column":36}},"19":{"start":{"line":39,"column":0},"end":{"line":39,"column":36}},"20":{"start":{"line":40,"column":0},"end":{"line":40,"column":25}},"21":{"start":{"line":46,"column":0},"end":{"line":46,"column":21}}},"fnMap":{},"branchMap":{"0":{"loc":{"start":{"line":2,"column":0},"end":{"line":6,"column":1}},"type":"if","locations":[{"start":{"line":2,"column":0},"end":{"line":6,"column":1}},{"start":{},"end":{}}],"line":2}},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":5,"7":5,"8":5,"9":5,"10":5,"11":5,"12":5,"13":5,"14":5,"15":5,"16":5,"17":5,"18":5,"19":5,"20":5,"21":5},"f":{},"b":{"0":[5,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"000d392bffbe5510c4733d45633cd435db14352c"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\config\\config.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\config\\config.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":27}},"1":{"start":{"line":3,"column":0},"end":{"line":28,"column":2}}},"fnMap":{},"branchMap":{"0":{"loc":{"start":{"line":14,"column":14},"end":{"line":14,"column":57}},"type":"binary-expr","locations":[{"start":{"line":14,"column":14},"end":{"line":14,"column":38}},{"start":{"line":14,"column":42},"end":{"line":14,"column":57}}],"line":14}},"s":{"0":5,"1":5},"f":{},"b":{"0":[5,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"e2c7758654be329740f3ec2fa84868f138b9713a"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\controllers\\authController.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\controllers\\authController.js","statementMap":{"0":{"start":{"line":1,"column":28},"end":{"line":1,"column":56}},"1":{"start":{"line":2,"column":22},"end":{"line":2,"column":47}},"2":{"start":{"line":3,"column":17},"end":{"line":3,"column":37}},"3":{"start":{"line":5,"column":25},"end":{"line":5,"column":55}},"4":{"start":{"line":6,"column":15},"end":{"line":6,"column":61}},"5":{"start":{"line":8,"column":0},"end":{"line":104,"column":2}},"6":{"start":{"line":10,"column":4},"end":{"line":25,"column":5}},"7":{"start":{"line":11,"column":46},"end":{"line":11,"column":54}},"8":{"start":{"line":13,"column":22},"end":{"line":13,"column":49}},"9":{"start":{"line":14,"column":6},"end":{"line":22,"column":9}},"10":{"start":{"line":24,"column":6},"end":{"line":24,"column":16}},"11":{"start":{"line":29,"column":32},"end":{"line":29,"column":40}},"12":{"start":{"line":30,"column":4},"end":{"line":67,"column":5}},"13":{"start":{"line":31,"column":6},"end":{"line":33,"column":7}},"14":{"start":{"line":32,"column":8},"end":{"line":32,"column":67}},"15":{"start":{"line":34,"column":25},"end":{"line":34,"column":53}},"16":{"start":{"line":35,"column":6},"end":{"line":37,"column":7}},"17":{"start":{"line":36,"column":8},"end":{"line":36,"column":70}},"18":{"start":{"line":38,"column":6},"end":{"line":40,"column":7}},"19":{"start":{"line":39,"column":8},"end":{"line":39,"column":70}},"20":{"start":{"line":41,"column":6},"end":{"line":46,"column":7}},"21":{"start":{"line":42,"column":8},"end":{"line":45,"column":10}},"22":{"start":{"line":47,"column":19},"end":{"line":47,"column":59}},"23":{"start":{"line":48,"column":6},"end":{"line":50,"column":7}},"24":{"start":{"line":49,"column":8},"end":{"line":49,"column":67}},"25":{"start":{"line":51,"column":30},"end":{"line":51,"column":70}},"26":{"start":{"line":52,"column":6},"end":{"line":54,"column":7}},"27":{"start":{"line":53,"column":8},"end":{"line":53,"column":78}},"28":{"start":{"line":55,"column":26},"end":{"line":55,"column":69}},"29":{"start":{"line":56,"column":6},"end":{"line":64,"column":9}},"30":{"start":{"line":66,"column":6},"end":{"line":66,"column":16}},"31":{"start":{"line":71,"column":25},"end":{"line":71,"column":33}},"32":{"start":{"line":72,"column":4},"end":{"line":102,"column":5}},"33":{"start":{"line":73,"column":6},"end":{"line":75,"column":7}},"34":{"start":{"line":74,"column":8},"end":{"line":74,"column":70}},"35":{"start":{"line":76,"column":21},"end":{"line":79,"column":8}},"36":{"start":{"line":80,"column":30},"end":{"line":80,"column":49}},"37":{"start":{"line":81,"column":17},"end":{"line":81,"column":57}},"38":{"start":{"line":82,"column":6},"end":{"line":89,"column":7}},"39":{"start":{"line":83,"column":8},"end":{"line":88,"column":11}},"40":{"start":{"line":90,"column":27},"end":{"line":90,"column":70}},"41":{"start":{"line":91,"column":6},"end":{"line":99,"column":9}},"42":{"start":{"line":101,"column":6},"end":{"line":101,"column":16}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":9,"column":2},"end":{"line":9,"column":3}},"loc":{"start":{"line":9,"column":40},"end":{"line":26,"column":3}},"line":9},"1":{"name":"(anonymous_1)","decl":{"start":{"line":28,"column":2},"end":{"line":28,"column":3}},"loc":{"start":{"line":28,"column":37},"end":{"line":68,"column":3}},"line":28},"2":{"name":"(anonymous_2)","decl":{"start":{"line":70,"column":2},"end":{"line":70,"column":3}},"loc":{"start":{"line":70,"column":43},"end":{"line":103,"column":3}},"line":70}},"branchMap":{"0":{"loc":{"start":{"line":31,"column":6},"end":{"line":33,"column":7}},"type":"if","locations":[{"start":{"line":31,"column":6},"end":{"line":33,"column":7}},{"start":{},"end":{}}],"line":31},"1":{"loc":{"start":{"line":35,"column":6},"end":{"line":37,"column":7}},"type":"if","locations":[{"start":{"line":35,"column":6},"end":{"line":37,"column":7}},{"start":{},"end":{}}],"line":35},"2":{"loc":{"start":{"line":38,"column":6},"end":{"line":40,"column":7}},"type":"if","locations":[{"start":{"line":38,"column":6},"end":{"line":40,"column":7}},{"start":{},"end":{}}],"line":38},"3":{"loc":{"start":{"line":41,"column":6},"end":{"line":46,"column":7}},"type":"if","locations":[{"start":{"line":41,"column":6},"end":{"line":46,"column":7}},{"start":{},"end":{}}],"line":41},"4":{"loc":{"start":{"line":48,"column":6},"end":{"line":50,"column":7}},"type":"if","locations":[{"start":{"line":48,"column":6},"end":{"line":50,"column":7}},{"start":{},"end":{}}],"line":48},"5":{"loc":{"start":{"line":52,"column":6},"end":{"line":54,"column":7}},"type":"if","locations":[{"start":{"line":52,"column":6},"end":{"line":54,"column":7}},{"start":{},"end":{}}],"line":52},"6":{"loc":{"start":{"line":73,"column":6},"end":{"line":75,"column":7}},"type":"if","locations":[{"start":{"line":73,"column":6},"end":{"line":75,"column":7}},{"start":{},"end":{}}],"line":73},"7":{"loc":{"start":{"line":82,"column":6},"end":{"line":89,"column":7}},"type":"if","locations":[{"start":{"line":82,"column":6},"end":{"line":89,"column":7}},{"start":{},"end":{}}],"line":82}},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":11,"7":11,"8":11,"9":1,"10":10,"11":11,"12":11,"13":11,"14":3,"15":8,"16":8,"17":1,"18":7,"19":2,"20":5,"21":1,"22":4,"23":3,"24":1,"25":2,"26":2,"27":1,"28":1,"29":1,"30":8,"31":3,"32":3,"33":3,"34":1,"35":2,"36":2,"37":2,"38":1,"39":1,"40":1,"41":1,"42":2},"f":{"0":11,"1":11,"2":3},"b":{"0":[3,8],"1":[1,7],"2":[2,5],"3":[1,4],"4":[1,2],"5":[1,1],"6":[1,2],"7":[1,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"34ddadc344c3140ae56a1c37c8f30f6882db5003"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\controllers\\bookingController.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\controllers\\bookingController.js","statementMap":{"0":{"start":{"line":1,"column":42},"end":{"line":1,"column":62}},"1":{"start":{"line":3,"column":0},"end":{"line":181,"column":2}},"2":{"start":{"line":6,"column":4},"end":{"line":24,"column":5}},"3":{"start":{"line":7,"column":21},"end":{"line":7,"column":32}},"4":{"start":{"line":9,"column":23},"end":{"line":19,"column":8}},"5":{"start":{"line":20,"column":6},"end":{"line":20,"column":37}},"6":{"start":{"line":23,"column":6},"end":{"line":23,"column":16}},"7":{"start":{"line":28,"column":4},"end":{"line":52,"column":5}},"8":{"start":{"line":29,"column":25},"end":{"line":29,"column":34}},"9":{"start":{"line":31,"column":22},"end":{"line":40,"column":7}},"10":{"start":{"line":43,"column":6},"end":{"line":46,"column":7}},"11":{"start":{"line":44,"column":8},"end":{"line":44,"column":35}},"12":{"start":{"line":48,"column":23},"end":{"line":48,"column":53}},"13":{"start":{"line":49,"column":6},"end":{"line":49,"column":37}},"14":{"start":{"line":51,"column":6},"end":{"line":51,"column":16}},"15":{"start":{"line":56,"column":4},"end":{"line":109,"column":5}},"16":{"start":{"line":57,"column":21},"end":{"line":57,"column":32}},"17":{"start":{"line":58,"column":52},"end":{"line":58,"column":60}},"18":{"start":{"line":61,"column":20},"end":{"line":61,"column":49}},"19":{"start":{"line":62,"column":6},"end":{"line":64,"column":7}},"20":{"start":{"line":63,"column":8},"end":{"line":63,"column":63}},"21":{"start":{"line":67,"column":30},"end":{"line":90,"column":8}},"22":{"start":{"line":92,"column":6},"end":{"line":97,"column":7}},"23":{"start":{"line":93,"column":8},"end":{"line":96,"column":10}},"24":{"start":{"line":99,"column":22},"end":{"line":105,"column":8}},"25":{"start":{"line":106,"column":6},"end":{"line":106,"column":68}},"26":{"start":{"line":108,"column":6},"end":{"line":108,"column":16}},"27":{"start":{"line":113,"column":4},"end":{"line":145,"column":5}},"28":{"start":{"line":114,"column":21},"end":{"line":114,"column":31}},"29":{"start":{"line":115,"column":51},"end":{"line":115,"column":59}},"30":{"start":{"line":116,"column":22},"end":{"line":116,"column":48}},"31":{"start":{"line":117,"column":6},"end":{"line":117,"column":77}},"32":{"start":{"line":117,"column":20},"end":{"line":117,"column":77}},"33":{"start":{"line":120,"column":6},"end":{"line":125,"column":7}},"34":{"start":{"line":121,"column":8},"end":{"line":124,"column":10}},"35":{"start":{"line":128,"column":6},"end":{"line":133,"column":7}},"36":{"start":{"line":129,"column":8},"end":{"line":132,"column":10}},"37":{"start":{"line":135,"column":6},"end":{"line":140,"column":9}},"38":{"start":{"line":142,"column":6},"end":{"line":142,"column":59}},"39":{"start":{"line":144,"column":6},"end":{"line":144,"column":16}},"40":{"start":{"line":149,"column":4},"end":{"line":167,"column":5}},"41":{"start":{"line":150,"column":21},"end":{"line":150,"column":31}},"42":{"start":{"line":151,"column":25},"end":{"line":151,"column":33}},"43":{"start":{"line":153,"column":22},"end":{"line":153,"column":48}},"44":{"start":{"line":154,"column":6},"end":{"line":154,"column":77}},"45":{"start":{"line":154,"column":20},"end":{"line":154,"column":77}},"46":{"start":{"line":157,"column":28},"end":{"line":157,"column":64}},"47":{"start":{"line":158,"column":6},"end":{"line":160,"column":7}},"48":{"start":{"line":159,"column":8},"end":{"line":159,"column":70}},"49":{"start":{"line":162,"column":6},"end":{"line":162,"column":39}},"50":{"start":{"line":164,"column":6},"end":{"line":164,"column":75}},"51":{"start":{"line":166,"column":6},"end":{"line":166,"column":16}},"52":{"start":{"line":171,"column":4},"end":{"line":179,"column":5}},"53":{"start":{"line":172,"column":21},"end":{"line":172,"column":31}},"54":{"start":{"line":173,"column":22},"end":{"line":173,"column":48}},"55":{"start":{"line":174,"column":6},"end":{"line":174,"column":77}},"56":{"start":{"line":174,"column":20},"end":{"line":174,"column":77}},"57":{"start":{"line":175,"column":6},"end":{"line":175,"column":30}},"58":{"start":{"line":176,"column":6},"end":{"line":176,"column":59}},"59":{"start":{"line":178,"column":6},"end":{"line":178,"column":16}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":2},"end":{"line":5,"column":3}},"loc":{"start":{"line":5,"column":45},"end":{"line":25,"column":3}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":27,"column":2},"end":{"line":27,"column":3}},"loc":{"start":{"line":27,"column":46},"end":{"line":53,"column":3}},"line":27},"2":{"name":"(anonymous_2)","decl":{"start":{"line":55,"column":2},"end":{"line":55,"column":3}},"loc":{"start":{"line":55,"column":45},"end":{"line":110,"column":3}},"line":55},"3":{"name":"(anonymous_3)","decl":{"start":{"line":112,"column":2},"end":{"line":112,"column":3}},"loc":{"start":{"line":112,"column":45},"end":{"line":146,"column":3}},"line":112},"4":{"name":"(anonymous_4)","decl":{"start":{"line":148,"column":2},"end":{"line":148,"column":3}},"loc":{"start":{"line":148,"column":51},"end":{"line":168,"column":3}},"line":148},"5":{"name":"(anonymous_5)","decl":{"start":{"line":170,"column":2},"end":{"line":170,"column":3}},"loc":{"start":{"line":170,"column":45},"end":{"line":180,"column":3}},"line":170}},"branchMap":{"0":{"loc":{"start":{"line":43,"column":6},"end":{"line":46,"column":7}},"type":"if","locations":[{"start":{"line":43,"column":6},"end":{"line":46,"column":7}},{"start":{},"end":{}}],"line":43},"1":{"loc":{"start":{"line":62,"column":6},"end":{"line":64,"column":7}},"type":"if","locations":[{"start":{"line":62,"column":6},"end":{"line":64,"column":7}},{"start":{},"end":{}}],"line":62},"2":{"loc":{"start":{"line":92,"column":6},"end":{"line":97,"column":7}},"type":"if","locations":[{"start":{"line":92,"column":6},"end":{"line":97,"column":7}},{"start":{},"end":{}}],"line":92},"3":{"loc":{"start":{"line":117,"column":6},"end":{"line":117,"column":77}},"type":"if","locations":[{"start":{"line":117,"column":6},"end":{"line":117,"column":77}},{"start":{},"end":{}}],"line":117},"4":{"loc":{"start":{"line":120,"column":6},"end":{"line":125,"column":7}},"type":"if","locations":[{"start":{"line":120,"column":6},"end":{"line":125,"column":7}},{"start":{},"end":{}}],"line":120},"5":{"loc":{"start":{"line":120,"column":10},"end":{"line":120,"column":69}},"type":"binary-expr","locations":[{"start":{"line":120,"column":10},"end":{"line":120,"column":35}},{"start":{"line":120,"column":39},"end":{"line":120,"column":69}}],"line":120},"6":{"loc":{"start":{"line":128,"column":6},"end":{"line":133,"column":7}},"type":"if","locations":[{"start":{"line":128,"column":6},"end":{"line":133,"column":7}},{"start":{},"end":{}}],"line":128},"7":{"loc":{"start":{"line":136,"column":14},"end":{"line":136,"column":34}},"type":"binary-expr","locations":[{"start":{"line":136,"column":14},"end":{"line":136,"column":18}},{"start":{"line":136,"column":22},"end":{"line":136,"column":34}}],"line":136},"8":{"loc":{"start":{"line":137,"column":19},"end":{"line":137,"column":49}},"type":"binary-expr","locations":[{"start":{"line":137,"column":19},"end":{"line":137,"column":28}},{"start":{"line":137,"column":32},"end":{"line":137,"column":49}}],"line":137},"9":{"loc":{"start":{"line":138,"column":17},"end":{"line":138,"column":43}},"type":"binary-expr","locations":[{"start":{"line":138,"column":17},"end":{"line":138,"column":24}},{"start":{"line":138,"column":28},"end":{"line":138,"column":43}}],"line":138},"10":{"loc":{"start":{"line":139,"column":16},"end":{"line":139,"column":40}},"type":"binary-expr","locations":[{"start":{"line":139,"column":16},"end":{"line":139,"column":22}},{"start":{"line":139,"column":26},"end":{"line":139,"column":40}}],"line":139},"11":{"loc":{"start":{"line":154,"column":6},"end":{"line":154,"column":77}},"type":"if","locations":[{"start":{"line":154,"column":6},"end":{"line":154,"column":77}},{"start":{},"end":{}}],"line":154},"12":{"loc":{"start":{"line":158,"column":6},"end":{"line":160,"column":7}},"type":"if","locations":[{"start":{"line":158,"column":6},"end":{"line":160,"column":7}},{"start":{},"end":{}}],"line":158},"13":{"loc":{"start":{"line":174,"column":6},"end":{"line":174,"column":77}},"type":"if","locations":[{"start":{"line":174,"column":6},"end":{"line":174,"column":77}},{"start":{},"end":{}}],"line":174}},"s":{"0":5,"1":5,"2":1,"3":1,"4":1,"5":1,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":4,"16":4,"17":4,"18":4,"19":4,"20":1,"21":3,"22":3,"23":1,"24":2,"25":2,"26":2,"27":1,"28":1,"29":1,"30":1,"31":1,"32":0,"33":1,"34":0,"35":1,"36":0,"37":1,"38":1,"39":0,"40":2,"41":2,"42":2,"43":2,"44":2,"45":1,"46":1,"47":1,"48":0,"49":1,"50":1,"51":1,"52":2,"53":2,"54":2,"55":2,"56":1,"57":1,"58":1,"59":1},"f":{"0":1,"1":0,"2":4,"3":1,"4":2,"5":2},"b":{"0":[0,0],"1":[1,3],"2":[1,2],"3":[0,1],"4":[0,1],"5":[1,1],"6":[0,1],"7":[1,0],"8":[1,0],"9":[1,0],"10":[1,1],"11":[1,1],"12":[0,1],"13":[1,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"6d7f6093873740359575090df126a34321c27831"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\controllers\\courtController.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\controllers\\courtController.js","statementMap":{"0":{"start":{"line":1,"column":18},"end":{"line":1,"column":38}},"1":{"start":{"line":3,"column":0},"end":{"line":84,"column":2}},"2":{"start":{"line":5,"column":4},"end":{"line":14,"column":5}},"3":{"start":{"line":6,"column":33},"end":{"line":6,"column":42}},"4":{"start":{"line":8,"column":21},"end":{"line":10,"column":8}},"5":{"start":{"line":11,"column":6},"end":{"line":11,"column":35}},"6":{"start":{"line":13,"column":6},"end":{"line":13,"column":16}},"7":{"start":{"line":18,"column":4},"end":{"line":27,"column":5}},"8":{"start":{"line":19,"column":22},"end":{"line":19,"column":35}},"9":{"start":{"line":20,"column":20},"end":{"line":20,"column":49}},"10":{"start":{"line":21,"column":6},"end":{"line":23,"column":7}},"11":{"start":{"line":22,"column":8},"end":{"line":22,"column":63}},"12":{"start":{"line":24,"column":6},"end":{"line":24,"column":34}},"13":{"start":{"line":26,"column":6},"end":{"line":26,"column":16}},"14":{"start":{"line":31,"column":4},"end":{"line":45,"column":5}},"15":{"start":{"line":33,"column":8},"end":{"line":33,"column":16}},"16":{"start":{"line":34,"column":23},"end":{"line":41,"column":8}},"17":{"start":{"line":42,"column":6},"end":{"line":42,"column":37}},"18":{"start":{"line":44,"column":6},"end":{"line":44,"column":16}},"19":{"start":{"line":49,"column":4},"end":{"line":68,"column":5}},"20":{"start":{"line":50,"column":22},"end":{"line":50,"column":35}},"21":{"start":{"line":52,"column":8},"end":{"line":52,"column":16}},"22":{"start":{"line":53,"column":20},"end":{"line":53,"column":49}},"23":{"start":{"line":54,"column":6},"end":{"line":56,"column":7}},"24":{"start":{"line":55,"column":8},"end":{"line":55,"column":63}},"25":{"start":{"line":57,"column":6},"end":{"line":64,"column":9}},"26":{"start":{"line":65,"column":6},"end":{"line":65,"column":70}},"27":{"start":{"line":67,"column":6},"end":{"line":67,"column":16}},"28":{"start":{"line":72,"column":4},"end":{"line":82,"column":5}},"29":{"start":{"line":73,"column":22},"end":{"line":73,"column":35}},"30":{"start":{"line":74,"column":20},"end":{"line":74,"column":49}},"31":{"start":{"line":75,"column":6},"end":{"line":77,"column":7}},"32":{"start":{"line":76,"column":8},"end":{"line":76,"column":63}},"33":{"start":{"line":78,"column":6},"end":{"line":78,"column":28}},"34":{"start":{"line":79,"column":6},"end":{"line":79,"column":70}},"35":{"start":{"line":81,"column":6},"end":{"line":81,"column":16}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":4,"column":2},"end":{"line":4,"column":3}},"loc":{"start":{"line":4,"column":44},"end":{"line":15,"column":3}},"line":4},"1":{"name":"(anonymous_1)","decl":{"start":{"line":17,"column":2},"end":{"line":17,"column":3}},"loc":{"start":{"line":17,"column":44},"end":{"line":28,"column":3}},"line":17},"2":{"name":"(anonymous_2)","decl":{"start":{"line":30,"column":2},"end":{"line":30,"column":3}},"loc":{"start":{"line":30,"column":43},"end":{"line":46,"column":3}},"line":30},"3":{"name":"(anonymous_3)","decl":{"start":{"line":48,"column":2},"end":{"line":48,"column":3}},"loc":{"start":{"line":48,"column":43},"end":{"line":69,"column":3}},"line":48},"4":{"name":"(anonymous_4)","decl":{"start":{"line":71,"column":2},"end":{"line":71,"column":3}},"loc":{"start":{"line":71,"column":43},"end":{"line":83,"column":3}},"line":71}},"branchMap":{"0":{"loc":{"start":{"line":6,"column":14},"end":{"line":6,"column":28}},"type":"default-arg","locations":[{"start":{"line":6,"column":22},"end":{"line":6,"column":28}}],"line":6},"1":{"loc":{"start":{"line":21,"column":6},"end":{"line":23,"column":7}},"type":"if","locations":[{"start":{"line":21,"column":6},"end":{"line":23,"column":7}},{"start":{},"end":{}}],"line":21},"2":{"loc":{"start":{"line":54,"column":6},"end":{"line":56,"column":7}},"type":"if","locations":[{"start":{"line":54,"column":6},"end":{"line":56,"column":7}},{"start":{},"end":{}}],"line":54},"3":{"loc":{"start":{"line":75,"column":6},"end":{"line":77,"column":7}},"type":"if","locations":[{"start":{"line":75,"column":6},"end":{"line":77,"column":7}},{"start":{},"end":{}}],"line":75}},"s":{"0":5,"1":5,"2":2,"3":2,"4":2,"5":2,"6":0,"7":2,"8":2,"9":2,"10":2,"11":1,"12":1,"13":1,"14":2,"15":2,"16":2,"17":1,"18":1,"19":2,"20":2,"21":2,"22":2,"23":2,"24":1,"25":1,"26":1,"27":1,"28":2,"29":2,"30":2,"31":2,"32":1,"33":1,"34":1,"35":1},"f":{"0":2,"1":2,"2":2,"3":2,"4":2},"b":{"0":[1],"1":[1,1],"2":[1,1],"3":[1,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"5b396bb67d5c7bbe9ca888b3fbb81f043d3b9923"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\controllers\\paymentController.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\controllers\\paymentController.js","statementMap":{"0":{"start":{"line":1,"column":36},"end":{"line":1,"column":56}},"1":{"start":{"line":2,"column":23},"end":{"line":2,"column":49}},"2":{"start":{"line":3,"column":22},"end":{"line":3,"column":47}},"3":{"start":{"line":4,"column":19},"end":{"line":4,"column":39}},"4":{"start":{"line":5,"column":14},"end":{"line":5,"column":30}},"5":{"start":{"line":6,"column":14},"end":{"line":6,"column":30}},"6":{"start":{"line":8,"column":0},"end":{"line":160,"column":2}},"7":{"start":{"line":10,"column":4},"end":{"line":105,"column":5}},"8":{"start":{"line":11,"column":6},"end":{"line":11,"column":51}},"9":{"start":{"line":12,"column":6},"end":{"line":12,"column":45}},"10":{"start":{"line":14,"column":6},"end":{"line":19,"column":7}},"11":{"start":{"line":15,"column":8},"end":{"line":18,"column":10}},"12":{"start":{"line":20,"column":6},"end":{"line":20,"column":75}},"13":{"start":{"line":21,"column":17},"end":{"line":26,"column":8}},"14":{"start":{"line":27,"column":6},"end":{"line":27,"column":45}},"15":{"start":{"line":28,"column":28},"end":{"line":28,"column":36}},"16":{"start":{"line":29,"column":6},"end":{"line":31,"column":7}},"17":{"start":{"line":30,"column":8},"end":{"line":30,"column":73}},"18":{"start":{"line":32,"column":6},"end":{"line":32,"column":43}},"19":{"start":{"line":33,"column":6},"end":{"line":33,"column":53}},"20":{"start":{"line":34,"column":6},"end":{"line":36,"column":7}},"21":{"start":{"line":35,"column":8},"end":{"line":35,"column":74}},"22":{"start":{"line":37,"column":6},"end":{"line":41,"column":7}},"23":{"start":{"line":38,"column":8},"end":{"line":40,"column":61}},"24":{"start":{"line":43,"column":22},"end":{"line":48,"column":8}},"25":{"start":{"line":50,"column":6},"end":{"line":52,"column":7}},"26":{"start":{"line":51,"column":8},"end":{"line":51,"column":70}},"27":{"start":{"line":54,"column":22},"end":{"line":54,"column":78}},"28":{"start":{"line":55,"column":6},"end":{"line":57,"column":7}},"29":{"start":{"line":56,"column":8},"end":{"line":56,"column":79}},"30":{"start":{"line":58,"column":21},"end":{"line":58,"column":26}},"31":{"start":{"line":59,"column":22},"end":{"line":74,"column":7}},"32":{"start":{"line":76,"column":6},"end":{"line":90,"column":7}},"33":{"start":{"line":78,"column":8},"end":{"line":78,"column":62}},"34":{"start":{"line":79,"column":8},"end":{"line":79,"column":70}},"35":{"start":{"line":81,"column":8},"end":{"line":85,"column":9}},"36":{"start":{"line":82,"column":10},"end":{"line":84,"column":12}},"37":{"start":{"line":88,"column":8},"end":{"line":88,"column":76}},"38":{"start":{"line":89,"column":8},"end":{"line":89,"column":18}},"39":{"start":{"line":91,"column":29},"end":{"line":91,"column":46}},"40":{"start":{"line":94,"column":6},"end":{"line":98,"column":9}},"41":{"start":{"line":100,"column":6},"end":{"line":100,"column":72}},"42":{"start":{"line":102,"column":6},"end":{"line":102,"column":52}},"43":{"start":{"line":103,"column":6},"end":{"line":103,"column":40}},"44":{"start":{"line":104,"column":6},"end":{"line":104,"column":16}},"45":{"start":{"line":110,"column":4},"end":{"line":158,"column":5}},"46":{"start":{"line":111,"column":26},"end":{"line":111,"column":34}},"47":{"start":{"line":114,"column":20},"end":{"line":118,"column":8}},"48":{"start":{"line":119,"column":6},"end":{"line":121,"column":7}},"49":{"start":{"line":120,"column":8},"end":{"line":120,"column":68}},"50":{"start":{"line":122,"column":6},"end":{"line":122,"column":38}},"51":{"start":{"line":123,"column":6},"end":{"line":123,"column":43}},"52":{"start":{"line":124,"column":6},"end":{"line":124,"column":70}},"53":{"start":{"line":126,"column":6},"end":{"line":128,"column":7}},"54":{"start":{"line":127,"column":8},"end":{"line":127,"column":77}},"55":{"start":{"line":130,"column":24},"end":{"line":130,"column":55}},"56":{"start":{"line":131,"column":30},"end":{"line":131,"column":77}},"57":{"start":{"line":132,"column":6},"end":{"line":132,"column":61}},"58":{"start":{"line":133,"column":23},"end":{"line":140,"column":7}},"59":{"start":{"line":141,"column":6},"end":{"line":141,"column":49}},"60":{"start":{"line":143,"column":6},"end":{"line":155,"column":7}},"61":{"start":{"line":145,"column":8},"end":{"line":148,"column":11}},"62":{"start":{"line":149,"column":8},"end":{"line":149,"column":75}},"63":{"start":{"line":151,"column":8},"end":{"line":154,"column":11}},"64":{"start":{"line":157,"column":6},"end":{"line":157,"column":16}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":9,"column":2},"end":{"line":9,"column":3}},"loc":{"start":{"line":9,"column":51},"end":{"line":106,"column":3}},"line":9},"1":{"name":"(anonymous_1)","decl":{"start":{"line":108,"column":2},"end":{"line":108,"column":3}},"loc":{"start":{"line":108,"column":46},"end":{"line":159,"column":3}},"line":108}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":6},"end":{"line":19,"column":7}},"type":"if","locations":[{"start":{"line":14,"column":6},"end":{"line":19,"column":7}},{"start":{},"end":{}}],"line":14},"1":{"loc":{"start":{"line":29,"column":6},"end":{"line":31,"column":7}},"type":"if","locations":[{"start":{"line":29,"column":6},"end":{"line":31,"column":7}},{"start":{},"end":{}}],"line":29},"2":{"loc":{"start":{"line":34,"column":6},"end":{"line":36,"column":7}},"type":"if","locations":[{"start":{"line":34,"column":6},"end":{"line":36,"column":7}},{"start":{},"end":{}}],"line":34},"3":{"loc":{"start":{"line":37,"column":6},"end":{"line":41,"column":7}},"type":"if","locations":[{"start":{"line":37,"column":6},"end":{"line":41,"column":7}},{"start":{},"end":{}}],"line":37},"4":{"loc":{"start":{"line":50,"column":6},"end":{"line":52,"column":7}},"type":"if","locations":[{"start":{"line":50,"column":6},"end":{"line":52,"column":7}},{"start":{},"end":{}}],"line":50},"5":{"loc":{"start":{"line":55,"column":6},"end":{"line":57,"column":7}},"type":"if","locations":[{"start":{"line":55,"column":6},"end":{"line":57,"column":7}},{"start":{},"end":{}}],"line":55},"6":{"loc":{"start":{"line":81,"column":8},"end":{"line":85,"column":9}},"type":"if","locations":[{"start":{"line":81,"column":8},"end":{"line":85,"column":9}},{"start":{},"end":{}}],"line":81},"7":{"loc":{"start":{"line":88,"column":41},"end":{"line":88,"column":74}},"type":"binary-expr","locations":[{"start":{"line":88,"column":41},"end":{"line":88,"column":59}},{"start":{"line":88,"column":63},"end":{"line":88,"column":74}}],"line":88},"8":{"loc":{"start":{"line":119,"column":6},"end":{"line":121,"column":7}},"type":"if","locations":[{"start":{"line":119,"column":6},"end":{"line":121,"column":7}},{"start":{},"end":{}}],"line":119},"9":{"loc":{"start":{"line":126,"column":6},"end":{"line":128,"column":7}},"type":"if","locations":[{"start":{"line":126,"column":6},"end":{"line":128,"column":7}},{"start":{},"end":{}}],"line":126},"10":{"loc":{"start":{"line":143,"column":6},"end":{"line":155,"column":7}},"type":"if","locations":[{"start":{"line":143,"column":6},"end":{"line":155,"column":7}},{"start":{"line":150,"column":13},"end":{"line":155,"column":7}}],"line":143},"11":{"loc":{"start":{"line":143,"column":10},"end":{"line":143,"column":77}},"type":"binary-expr","locations":[{"start":{"line":143,"column":10},"end":{"line":143,"column":47}},{"start":{"line":143,"column":51},"end":{"line":143,"column":77}}],"line":143}},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":5,"7":1,"8":1,"9":1,"10":1,"11":0,"12":1,"13":1,"14":1,"15":1,"16":1,"17":0,"18":1,"19":1,"20":1,"21":0,"22":1,"23":0,"24":1,"25":1,"26":0,"27":1,"28":1,"29":0,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":0,"37":0,"38":0,"39":1,"40":1,"41":1,"42":0,"43":0,"44":0,"45":1,"46":1,"47":1,"48":1,"49":0,"50":1,"51":1,"52":1,"53":1,"54":0,"55":1,"56":1,"57":1,"58":1,"59":1,"60":1,"61":1,"62":1,"63":0,"64":0},"f":{"0":1,"1":1},"b":{"0":[0,1],"1":[0,1],"2":[0,1],"3":[0,1],"4":[0,1],"5":[0,1],"6":[0,1],"7":[0,0],"8":[0,1],"9":[0,1],"10":[1,0],"11":[1,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"ab0190e3e81fd9ea9ea2db3d32af0c676d4b0897"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\controllers\\publicController.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\controllers\\publicController.js","statementMap":{"0":{"start":{"line":1,"column":18},"end":{"line":1,"column":38}},"1":{"start":{"line":2,"column":15},"end":{"line":2,"column":35}},"2":{"start":{"line":4,"column":0},"end":{"line":52,"column":2}},"3":{"start":{"line":6,"column":4},"end":{"line":37,"column":5}},"4":{"start":{"line":7,"column":62},"end":{"line":7,"column":71}},"5":{"start":{"line":8,"column":20},"end":{"line":8,"column":22}},"6":{"start":{"line":10,"column":6},"end":{"line":15,"column":7}},"7":{"start":{"line":11,"column":8},"end":{"line":14,"column":10}},"8":{"start":{"line":17,"column":6},"end":{"line":19,"column":7}},"9":{"start":{"line":18,"column":8},"end":{"line":18,"column":57}},"10":{"start":{"line":21,"column":21},"end":{"line":21,"column":39}},"11":{"start":{"line":22,"column":30},"end":{"line":26,"column":8}},"12":{"start":{"line":27,"column":6},"end":{"line":34,"column":9}},"13":{"start":{"line":36,"column":6},"end":{"line":36,"column":16}},"14":{"start":{"line":41,"column":4},"end":{"line":50,"column":5}},"15":{"start":{"line":42,"column":22},"end":{"line":42,"column":35}},"16":{"start":{"line":43,"column":20},"end":{"line":43,"column":49}},"17":{"start":{"line":44,"column":6},"end":{"line":46,"column":7}},"18":{"start":{"line":45,"column":8},"end":{"line":45,"column":63}},"19":{"start":{"line":47,"column":6},"end":{"line":47,"column":34}},"20":{"start":{"line":49,"column":6},"end":{"line":49,"column":16}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":2},"end":{"line":5,"column":3}},"loc":{"start":{"line":5,"column":41},"end":{"line":38,"column":3}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":40,"column":2},"end":{"line":40,"column":3}},"loc":{"start":{"line":40,"column":45},"end":{"line":51,"column":3}},"line":40}},"branchMap":{"0":{"loc":{"start":{"line":7,"column":14},"end":{"line":7,"column":25}},"type":"default-arg","locations":[{"start":{"line":7,"column":23},"end":{"line":7,"column":25}}],"line":7},"1":{"loc":{"start":{"line":7,"column":37},"end":{"line":7,"column":45}},"type":"default-arg","locations":[{"start":{"line":7,"column":44},"end":{"line":7,"column":45}}],"line":7},"2":{"loc":{"start":{"line":7,"column":47},"end":{"line":7,"column":57}},"type":"default-arg","locations":[{"start":{"line":7,"column":55},"end":{"line":7,"column":57}}],"line":7},"3":{"loc":{"start":{"line":10,"column":6},"end":{"line":15,"column":7}},"type":"if","locations":[{"start":{"line":10,"column":6},"end":{"line":15,"column":7}},{"start":{},"end":{}}],"line":10},"4":{"loc":{"start":{"line":10,"column":10},"end":{"line":10,"column":29}},"type":"binary-expr","locations":[{"start":{"line":10,"column":10},"end":{"line":10,"column":16}},{"start":{"line":10,"column":20},"end":{"line":10,"column":29}}],"line":10},"5":{"loc":{"start":{"line":17,"column":6},"end":{"line":19,"column":7}},"type":"if","locations":[{"start":{"line":17,"column":6},"end":{"line":19,"column":7}},{"start":{},"end":{}}],"line":17},"6":{"loc":{"start":{"line":44,"column":6},"end":{"line":46,"column":7}},"type":"if","locations":[{"start":{"line":44,"column":6},"end":{"line":46,"column":7}},{"start":{},"end":{}}],"line":44}},"s":{"0":5,"1":5,"2":5,"3":3,"4":3,"5":3,"6":3,"7":1,"8":3,"9":1,"10":3,"11":3,"12":3,"13":0,"14":2,"15":2,"16":2,"17":2,"18":1,"19":1,"20":1},"f":{"0":3,"1":2},"b":{"0":[2],"1":[3],"2":[3],"3":[1,2],"4":[3,1],"5":[1,2],"6":[1,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"836d120fbdc707bb804bde9c2ef78efd96fc6b93"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\helpers\\AskGemini.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\helpers\\AskGemini.js","statementMap":{"0":{"start":{"line":1,"column":31},"end":{"line":1,"column":63}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":27}},"2":{"start":{"line":4,"column":14},"end":{"line":4,"column":70}},"3":{"start":{"line":7,"column":2},"end":{"line":7,"column":40}},"4":{"start":{"line":9,"column":16},"end":{"line":9,"column":78}},"5":{"start":{"line":11,"column":17},"end":{"line":11,"column":52}},"6":{"start":{"line":12,"column":19},"end":{"line":12,"column":40}},"7":{"start":{"line":13,"column":2},"end":{"line":13,"column":25}},"8":{"start":{"line":16,"column":0},"end":{"line":16,"column":31}}},"fnMap":{"0":{"name":"askGemini","decl":{"start":{"line":6,"column":15},"end":{"line":6,"column":24}},"loc":{"start":{"line":6,"column":33},"end":{"line":14,"column":1}},"line":6}},"branchMap":{},"s":{"0":6,"1":6,"2":6,"3":2,"4":2,"5":2,"6":2,"7":2,"8":6},"f":{"0":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"d6530c02749668a084964f534c0f38b9e11d8c6f"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\helpers\\bcrypt.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\helpers\\bcrypt.js","statementMap":{"0":{"start":{"line":1,"column":15},"end":{"line":1,"column":34}},"1":{"start":{"line":3,"column":21},"end":{"line":6,"column":1}},"2":{"start":{"line":4,"column":15},"end":{"line":4,"column":37}},"3":{"start":{"line":5,"column":2},"end":{"line":5,"column":41}},"4":{"start":{"line":8,"column":24},"end":{"line":10,"column":1}},"5":{"start":{"line":9,"column":2},"end":{"line":9,"column":59}},"6":{"start":{"line":12,"column":0},"end":{"line":15,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":3,"column":21},"end":{"line":3,"column":22}},"loc":{"start":{"line":3,"column":35},"end":{"line":6,"column":1}},"line":3},"1":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":24},"end":{"line":8,"column":25}},"loc":{"start":{"line":8,"column":59},"end":{"line":10,"column":1}},"line":8}},"branchMap":{},"s":{"0":5,"1":5,"2":7,"3":7,"4":5,"5":2,"6":5},"f":{"0":7,"1":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"50033d76ca0fbc6531fbc83e95a9f0e4ccf68e1a"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\helpers\\jwt.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\helpers\\jwt.js","statementMap":{"0":{"start":{"line":1,"column":12},"end":{"line":1,"column":35}},"1":{"start":{"line":2,"column":15},"end":{"line":2,"column":54}},"2":{"start":{"line":5,"column":2},"end":{"line":5,"column":35}},"3":{"start":{"line":9,"column":2},"end":{"line":9,"column":35}},"4":{"start":{"line":12,"column":0},"end":{"line":12,"column":44}}},"fnMap":{"0":{"name":"signToken","decl":{"start":{"line":4,"column":9},"end":{"line":4,"column":18}},"loc":{"start":{"line":4,"column":28},"end":{"line":6,"column":1}},"line":4},"1":{"name":"verifyToken","decl":{"start":{"line":8,"column":9},"end":{"line":8,"column":20}},"loc":{"start":{"line":8,"column":28},"end":{"line":10,"column":1}},"line":8}},"branchMap":{"0":{"loc":{"start":{"line":2,"column":15},"end":{"line":2,"column":54}},"type":"binary-expr","locations":[{"start":{"line":2,"column":15},"end":{"line":2,"column":37}},{"start":{"line":2,"column":41},"end":{"line":2,"column":54}}],"line":2}},"s":{"0":5,"1":5,"2":6,"3":23,"4":5},"f":{"0":6,"1":23},"b":{"0":[5,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"6534b0e08bd4f7fb38c59fd983a0fd6f33d5ee48"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\middleware\\authentication.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\middleware\\authentication.js","statementMap":{"0":{"start":{"line":1,"column":17},"end":{"line":1,"column":37}},"1":{"start":{"line":2,"column":24},"end":{"line":2,"column":49}},"2":{"start":{"line":5,"column":0},"end":{"line":32,"column":2}},"3":{"start":{"line":7,"column":2},"end":{"line":7,"column":39}},"4":{"start":{"line":8,"column":22},"end":{"line":8,"column":47}},"5":{"start":{"line":9,"column":2},"end":{"line":9,"column":55}},"6":{"start":{"line":10,"column":2},"end":{"line":13,"column":3}},"7":{"start":{"line":11,"column":4},"end":{"line":11,"column":61}},"8":{"start":{"line":12,"column":4},"end":{"line":12,"column":11}},"9":{"start":{"line":14,"column":23},"end":{"line":14,"column":48}},"10":{"start":{"line":16,"column":2},"end":{"line":31,"column":3}},"11":{"start":{"line":18,"column":17},"end":{"line":18,"column":42}},"12":{"start":{"line":20,"column":17},"end":{"line":20,"column":45}},"13":{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},"14":{"start":{"line":23,"column":6},"end":{"line":23,"column":63}},"15":{"start":{"line":26,"column":4},"end":{"line":26,"column":20}},"16":{"start":{"line":28,"column":4},"end":{"line":28,"column":11}},"17":{"start":{"line":30,"column":4},"end":{"line":30,"column":14}}},"fnMap":{"0":{"name":"authentication","decl":{"start":{"line":5,"column":32},"end":{"line":5,"column":46}},"loc":{"start":{"line":5,"column":63},"end":{"line":32,"column":1}},"line":5}},"branchMap":{"0":{"loc":{"start":{"line":10,"column":2},"end":{"line":13,"column":3}},"type":"if","locations":[{"start":{"line":10,"column":2},"end":{"line":13,"column":3}},{"start":{},"end":{}}],"line":10},"1":{"loc":{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},"type":"if","locations":[{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},{"start":{},"end":{}}],"line":22}},"s":{"0":5,"1":5,"2":5,"3":25,"4":25,"5":25,"6":25,"7":2,"8":2,"9":23,"10":23,"11":23,"12":23,"13":23,"14":0,"15":23,"16":23,"17":0},"f":{"0":25},"b":{"0":[2,23],"1":[0,23]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"14da77e1df980b8d157f31c0d8ba2fda6ae487e4"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\middleware\\errorHandler.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\middleware\\errorHandler.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":17,"column":2}},"1":{"start":{"line":2,"column":2},"end":{"line":16,"column":3}},"2":{"start":{"line":3,"column":4},"end":{"line":3,"column":51}},"3":{"start":{"line":4,"column":9},"end":{"line":16,"column":3}},"4":{"start":{"line":5,"column":4},"end":{"line":5,"column":61}},"5":{"start":{"line":6,"column":9},"end":{"line":16,"column":3}},"6":{"start":{"line":7,"column":4},"end":{"line":7,"column":51}},"7":{"start":{"line":8,"column":9},"end":{"line":16,"column":3}},"8":{"start":{"line":9,"column":4},"end":{"line":9,"column":55}},"9":{"start":{"line":10,"column":9},"end":{"line":16,"column":3}},"10":{"start":{"line":11,"column":4},"end":{"line":11,"column":51}},"11":{"start":{"line":12,"column":9},"end":{"line":16,"column":3}},"12":{"start":{"line":13,"column":4},"end":{"line":13,"column":51}},"13":{"start":{"line":15,"column":4},"end":{"line":15,"column":63}}},"fnMap":{"0":{"name":"errorHandler","decl":{"start":{"line":1,"column":26},"end":{"line":1,"column":38}},"loc":{"start":{"line":1,"column":60},"end":{"line":17,"column":1}},"line":1}},"branchMap":{"0":{"loc":{"start":{"line":2,"column":2},"end":{"line":16,"column":3}},"type":"if","locations":[{"start":{"line":2,"column":2},"end":{"line":16,"column":3}},{"start":{"line":4,"column":9},"end":{"line":16,"column":3}}],"line":2},"1":{"loc":{"start":{"line":4,"column":9},"end":{"line":16,"column":3}},"type":"if","locations":[{"start":{"line":4,"column":9},"end":{"line":16,"column":3}},{"start":{"line":6,"column":9},"end":{"line":16,"column":3}}],"line":4},"2":{"loc":{"start":{"line":6,"column":9},"end":{"line":16,"column":3}},"type":"if","locations":[{"start":{"line":6,"column":9},"end":{"line":16,"column":3}},{"start":{"line":8,"column":9},"end":{"line":16,"column":3}}],"line":6},"3":{"loc":{"start":{"line":8,"column":9},"end":{"line":16,"column":3}},"type":"if","locations":[{"start":{"line":8,"column":9},"end":{"line":16,"column":3}},{"start":{"line":10,"column":9},"end":{"line":16,"column":3}}],"line":8},"4":{"loc":{"start":{"line":10,"column":9},"end":{"line":16,"column":3}},"type":"if","locations":[{"start":{"line":10,"column":9},"end":{"line":16,"column":3}},{"start":{"line":12,"column":9},"end":{"line":16,"column":3}}],"line":10},"5":{"loc":{"start":{"line":12,"column":9},"end":{"line":16,"column":3}},"type":"if","locations":[{"start":{"line":12,"column":9},"end":{"line":16,"column":3}},{"start":{"line":14,"column":9},"end":{"line":16,"column":3}}],"line":12}},"s":{"0":6,"1":33,"2":9,"3":24,"4":10,"5":14,"6":2,"7":12,"8":0,"9":12,"10":1,"11":11,"12":7,"13":4},"f":{"0":33},"b":{"0":[9,24],"1":[10,14],"2":[2,12],"3":[0,12],"4":[1,11],"5":[7,4]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"4e94ee1701bf744ffe2eb78ea8b4dc598e976939"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\middleware\\isAdmin.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\middleware\\isAdmin.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":9,"column":2}},"1":{"start":{"line":2,"column":2},"end":{"line":7,"column":3}},"2":{"start":{"line":3,"column":4},"end":{"line":6,"column":7}},"3":{"start":{"line":8,"column":2},"end":{"line":8,"column":9}}},"fnMap":{"0":{"name":"authorizeAdmin","decl":{"start":{"line":1,"column":26},"end":{"line":1,"column":40}},"loc":{"start":{"line":1,"column":57},"end":{"line":9,"column":1}},"line":1}},"branchMap":{"0":{"loc":{"start":{"line":2,"column":2},"end":{"line":7,"column":3}},"type":"if","locations":[{"start":{"line":2,"column":2},"end":{"line":7,"column":3}},{"start":{},"end":{}}],"line":2}},"s":{"0":5,"1":15,"2":1,"3":14},"f":{"0":15},"b":{"0":[1,14]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"021fcc2a01de06f830a89a80dda0c122c3f26aaf"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\models\\booking.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\models\\booking.js","statementMap":{"0":{"start":{"line":2,"column":18},"end":{"line":2,"column":38}},"1":{"start":{"line":3,"column":0},"end":{"line":45,"column":2}},"2":{"start":{"line":12,"column":6},"end":{"line":12,"column":63}},"3":{"start":{"line":13,"column":6},"end":{"line":13,"column":65}},"4":{"start":{"line":14,"column":6},"end":{"line":14,"column":66}},"5":{"start":{"line":17,"column":2},"end":{"line":43,"column":4}},"6":{"start":{"line":44,"column":2},"end":{"line":44,"column":17}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":3,"column":17},"end":{"line":3,"column":18}},"loc":{"start":{"line":3,"column":43},"end":{"line":45,"column":1}},"line":3},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":4},"end":{"line":10,"column":5}},"loc":{"start":{"line":10,"column":29},"end":{"line":15,"column":5}},"line":10}},"branchMap":{},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":5},"f":{"0":5,"1":5},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"449fca4e08aa5220429db7c4e3049c530093535c"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\models\\court.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\models\\court.js","statementMap":{"0":{"start":{"line":2,"column":18},"end":{"line":2,"column":38}},"1":{"start":{"line":3,"column":0},"end":{"line":96,"column":2}},"2":{"start":{"line":12,"column":6},"end":{"line":12,"column":63}},"3":{"start":{"line":15,"column":2},"end":{"line":94,"column":4}},"4":{"start":{"line":95,"column":2},"end":{"line":95,"column":15}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":3,"column":17},"end":{"line":3,"column":18}},"loc":{"start":{"line":3,"column":43},"end":{"line":96,"column":1}},"line":3},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":4},"end":{"line":10,"column":5}},"loc":{"start":{"line":10,"column":29},"end":{"line":13,"column":5}},"line":10}},"branchMap":{},"s":{"0":5,"1":5,"2":5,"3":5,"4":5},"f":{"0":5,"1":5},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"f29b020dec51c6b96faeec04331a510ad9ad911d"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\models\\index.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\models\\index.js","statementMap":{"0":{"start":{"line":3,"column":11},"end":{"line":3,"column":24}},"1":{"start":{"line":4,"column":13},"end":{"line":4,"column":28}},"2":{"start":{"line":5,"column":18},"end":{"line":5,"column":38}},"3":{"start":{"line":6,"column":16},"end":{"line":6,"column":34}},"4":{"start":{"line":7,"column":17},"end":{"line":7,"column":42}},"5":{"start":{"line":8,"column":12},"end":{"line":8,"column":49}},"6":{"start":{"line":9,"column":15},"end":{"line":9,"column":63}},"7":{"start":{"line":10,"column":11},"end":{"line":10,"column":13}},"8":{"start":{"line":13,"column":0},"end":{"line":22,"column":1}},"9":{"start":{"line":14,"column":2},"end":{"line":14,"column":74}},"10":{"start":{"line":16,"column":2},"end":{"line":21,"column":4}},"11":{"start":{"line":24,"column":0},"end":{"line":39,"column":5}},"12":{"start":{"line":26,"column":4},"end":{"line":31,"column":6}},"13":{"start":{"line":34,"column":18},"end":{"line":37,"column":5}},"14":{"start":{"line":38,"column":4},"end":{"line":38,"column":27}},"15":{"start":{"line":41,"column":0},"end":{"line":45,"column":3}},"16":{"start":{"line":42,"column":2},"end":{"line":44,"column":3}},"17":{"start":{"line":43,"column":4},"end":{"line":43,"column":32}},"18":{"start":{"line":47,"column":0},"end":{"line":47,"column":25}},"19":{"start":{"line":48,"column":0},"end":{"line":48,"column":25}},"20":{"start":{"line":50,"column":0},"end":{"line":50,"column":20}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":25,"column":10},"end":{"line":25,"column":11}},"loc":{"start":{"line":25,"column":20},"end":{"line":32,"column":3}},"line":25},"1":{"name":"(anonymous_1)","decl":{"start":{"line":33,"column":11},"end":{"line":33,"column":12}},"loc":{"start":{"line":33,"column":21},"end":{"line":39,"column":3}},"line":33},"2":{"name":"(anonymous_2)","decl":{"start":{"line":41,"column":24},"end":{"line":41,"column":25}},"loc":{"start":{"line":41,"column":39},"end":{"line":45,"column":1}},"line":41}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":12},"end":{"line":8,"column":49}},"type":"binary-expr","locations":[{"start":{"line":8,"column":12},"end":{"line":8,"column":32}},{"start":{"line":8,"column":36},"end":{"line":8,"column":49}}],"line":8},"1":{"loc":{"start":{"line":13,"column":0},"end":{"line":22,"column":1}},"type":"if","locations":[{"start":{"line":13,"column":0},"end":{"line":22,"column":1}},{"start":{"line":15,"column":7},"end":{"line":22,"column":1}}],"line":13},"2":{"loc":{"start":{"line":27,"column":6},"end":{"line":30,"column":37}},"type":"binary-expr","locations":[{"start":{"line":27,"column":6},"end":{"line":27,"column":29}},{"start":{"line":28,"column":6},"end":{"line":28,"column":23}},{"start":{"line":29,"column":6},"end":{"line":29,"column":30}},{"start":{"line":30,"column":6},"end":{"line":30,"column":37}}],"line":27},"3":{"loc":{"start":{"line":42,"column":2},"end":{"line":44,"column":3}},"type":"if","locations":[{"start":{"line":42,"column":2},"end":{"line":44,"column":3}},{"start":{},"end":{}}],"line":42}},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":5,"7":5,"8":5,"9":0,"10":5,"11":5,"12":25,"13":20,"14":20,"15":5,"16":20,"17":20,"18":5,"19":5,"20":5},"f":{"0":25,"1":20,"2":20},"b":{"0":[5,0],"1":[0,5],"2":[25,25,20,20],"3":[20,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"bbb962e3008b361b0cf268ef0ff931536db86700"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\models\\payment.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\models\\payment.js","statementMap":{"0":{"start":{"line":2,"column":18},"end":{"line":2,"column":38}},"1":{"start":{"line":3,"column":0},"end":{"line":72,"column":2}},"2":{"start":{"line":7,"column":6},"end":{"line":7,"column":69}},"3":{"start":{"line":10,"column":2},"end":{"line":70,"column":4}},"4":{"start":{"line":71,"column":2},"end":{"line":71,"column":17}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":3,"column":17},"end":{"line":3,"column":18}},"loc":{"start":{"line":3,"column":43},"end":{"line":72,"column":1}},"line":3},"1":{"name":"(anonymous_1)","decl":{"start":{"line":5,"column":4},"end":{"line":5,"column":5}},"loc":{"start":{"line":5,"column":29},"end":{"line":8,"column":5}},"line":5}},"branchMap":{},"s":{"0":5,"1":5,"2":5,"3":5,"4":5},"f":{"0":5,"1":5},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"793acf6c6e6aa95fa02217cbcf10d9799b5790d4"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\models\\user.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\models\\user.js","statementMap":{"0":{"start":{"line":2,"column":18},"end":{"line":2,"column":38}},"1":{"start":{"line":3,"column":25},"end":{"line":3,"column":53}},"2":{"start":{"line":4,"column":0},"end":{"line":74,"column":2}},"3":{"start":{"line":13,"column":6},"end":{"line":13,"column":61}},"4":{"start":{"line":16,"column":2},"end":{"line":69,"column":4}},"5":{"start":{"line":70,"column":2},"end":{"line":72,"column":5}},"6":{"start":{"line":71,"column":4},"end":{"line":71,"column":48}},"7":{"start":{"line":73,"column":2},"end":{"line":73,"column":14}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":4,"column":17},"end":{"line":4,"column":18}},"loc":{"start":{"line":4,"column":43},"end":{"line":74,"column":1}},"line":4},"1":{"name":"(anonymous_1)","decl":{"start":{"line":11,"column":4},"end":{"line":11,"column":5}},"loc":{"start":{"line":11,"column":29},"end":{"line":14,"column":5}},"line":11},"2":{"name":"(anonymous_2)","decl":{"start":{"line":70,"column":20},"end":{"line":70,"column":21}},"loc":{"start":{"line":70,"column":30},"end":{"line":72,"column":3}},"line":70}},"branchMap":{},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":7,"7":5},"f":{"0":5,"1":5,"2":7},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"059546d5f710e8d830442a7be083536f76d56c29"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\ai.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\ai.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":22},"end":{"line":2,"column":53}},"2":{"start":{"line":3,"column":21},"end":{"line":3,"column":58}},"3":{"start":{"line":5,"column":15},"end":{"line":5,"column":31}},"4":{"start":{"line":7,"column":0},"end":{"line":19,"column":3}},"5":{"start":{"line":8,"column":2},"end":{"line":18,"column":3}},"6":{"start":{"line":9,"column":43},"end":{"line":9,"column":51}},"7":{"start":{"line":10,"column":19},"end":{"line":12,"column":7}},"8":{"start":{"line":14,"column":27},"end":{"line":14,"column":50}},"9":{"start":{"line":15,"column":4},"end":{"line":15,"column":33}},"10":{"start":{"line":17,"column":4},"end":{"line":17,"column":14}},"11":{"start":{"line":21,"column":0},"end":{"line":21,"column":25}},"12":{"start":{"line":23,"column":0},"end":{"line":23,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":7,"column":26},"end":{"line":7,"column":27}},"loc":{"start":{"line":7,"column":52},"end":{"line":19,"column":1}},"line":7}},"branchMap":{},"s":{"0":6,"1":6,"2":6,"3":6,"4":6,"5":2,"6":2,"7":2,"8":2,"9":1,"10":1,"11":6,"12":6},"f":{"0":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"081575cbb47f2b83074713bd53f45f4d7975c3b6"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\auth.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\auth.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":15},"end":{"line":2,"column":31}},"2":{"start":{"line":3,"column":23},"end":{"line":3,"column":63}},"3":{"start":{"line":4,"column":21},"end":{"line":4,"column":58}},"4":{"start":{"line":6,"column":0},"end":{"line":6,"column":44}},"5":{"start":{"line":7,"column":0},"end":{"line":7,"column":50}},"6":{"start":{"line":8,"column":0},"end":{"line":8,"column":57}},"7":{"start":{"line":11,"column":0},"end":{"line":11,"column":25}},"8":{"start":{"line":13,"column":0},"end":{"line":13,"column":24}}},"fnMap":{},"branchMap":{},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":5,"7":5,"8":5},"f":{},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"85e63cec1f5e637043f310b3e77cd9f28aaa2a46"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\bookings.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\bookings.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":15},"end":{"line":2,"column":42}},"2":{"start":{"line":3,"column":26},"end":{"line":3,"column":69}},"3":{"start":{"line":4,"column":23},"end":{"line":4,"column":62}},"4":{"start":{"line":5,"column":16},"end":{"line":5,"column":48}},"5":{"start":{"line":6,"column":21},"end":{"line":6,"column":58}},"6":{"start":{"line":8,"column":0},"end":{"line":8,"column":27}},"7":{"start":{"line":10,"column":0},"end":{"line":10,"column":53}},"8":{"start":{"line":11,"column":0},"end":{"line":11,"column":50}},"9":{"start":{"line":12,"column":0},"end":{"line":12,"column":54}},"10":{"start":{"line":15,"column":0},"end":{"line":15,"column":59}},"11":{"start":{"line":16,"column":0},"end":{"line":16,"column":61}},"12":{"start":{"line":17,"column":0},"end":{"line":17,"column":64}},"13":{"start":{"line":18,"column":0},"end":{"line":18,"column":76}},"14":{"start":{"line":21,"column":0},"end":{"line":21,"column":25}},"15":{"start":{"line":23,"column":0},"end":{"line":23,"column":24}}},"fnMap":{},"branchMap":{},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":5,"7":5,"8":5,"9":5,"10":5,"11":5,"12":5,"13":5,"14":5,"15":5},"f":{},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"bf4c202c3d1485adad5c9006bd5c819334b3dfa3"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\courts.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\courts.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":15},"end":{"line":2,"column":31}},"2":{"start":{"line":3,"column":24},"end":{"line":3,"column":65}},"3":{"start":{"line":4,"column":23},"end":{"line":4,"column":62}},"4":{"start":{"line":5,"column":16},"end":{"line":5,"column":48}},"5":{"start":{"line":6,"column":21},"end":{"line":6,"column":58}},"6":{"start":{"line":9,"column":0},"end":{"line":9,"column":27}},"7":{"start":{"line":12,"column":0},"end":{"line":12,"column":55}},"8":{"start":{"line":15,"column":0},"end":{"line":15,"column":58}},"9":{"start":{"line":18,"column":0},"end":{"line":18,"column":55}},"10":{"start":{"line":21,"column":0},"end":{"line":21,"column":57}},"11":{"start":{"line":24,"column":0},"end":{"line":24,"column":60}},"12":{"start":{"line":27,"column":0},"end":{"line":27,"column":25}},"13":{"start":{"line":29,"column":0},"end":{"line":29,"column":24}}},"fnMap":{},"branchMap":{},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":5,"7":5,"8":5,"9":5,"10":5,"11":5,"12":5,"13":5},"f":{},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"3f9d9805e5073641676c34602680cec9e94d12b8"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\payments.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\payments.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":15},"end":{"line":2,"column":31}},"2":{"start":{"line":3,"column":23},"end":{"line":3,"column":62}},"3":{"start":{"line":4,"column":26},"end":{"line":4,"column":69}},"4":{"start":{"line":5,"column":21},"end":{"line":5,"column":58}},"5":{"start":{"line":7,"column":0},"end":{"line":7,"column":27}},"6":{"start":{"line":9,"column":0},"end":{"line":9,"column":62}},"7":{"start":{"line":10,"column":0},"end":{"line":10,"column":73}},"8":{"start":{"line":12,"column":0},"end":{"line":12,"column":25}},"9":{"start":{"line":14,"column":0},"end":{"line":14,"column":24}}},"fnMap":{},"branchMap":{},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":5,"7":5,"8":5,"9":5},"f":{},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"74b3fcb14ddcc92cc1926d022e792b2a0c98b2e2"} +,"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\public.js": {"path":"C:\\Users\\TUF GAMING\\Hacktiv8\\phase2\\week3\\IP-RMT63\\Server\\routes\\public.js","statementMap":{"0":{"start":{"line":1,"column":16},"end":{"line":1,"column":34}},"1":{"start":{"line":2,"column":15},"end":{"line":2,"column":31}},"2":{"start":{"line":3,"column":25},"end":{"line":3,"column":67}},"3":{"start":{"line":4,"column":21},"end":{"line":4,"column":58}},"4":{"start":{"line":8,"column":0},"end":{"line":8,"column":50}},"5":{"start":{"line":10,"column":0},"end":{"line":10,"column":58}},"6":{"start":{"line":13,"column":0},"end":{"line":13,"column":25}},"7":{"start":{"line":15,"column":0},"end":{"line":15,"column":24}}},"fnMap":{},"branchMap":{},"s":{"0":5,"1":5,"2":5,"3":5,"4":5,"5":5,"6":5,"7":5},"f":{},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"8919d864f235ea6e4637f158dfd29c60a1f503f2"} +} diff --git a/Server/coverage/lcov-report/Server/app.js.html b/Server/coverage/lcov-report/Server/app.js.html new file mode 100644 index 0000000..6459a78 --- /dev/null +++ b/Server/coverage/lcov-report/Server/app.js.html @@ -0,0 +1,223 @@ + + + + + + Code coverage report for Server/app.js + + + + + + + + + +
+
+

All files / Server app.js

+
+ +
+ 100% + Statements + 22/22 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 22/22 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +475x +5x +  +  +5x +  +  +5x +5x +5x +5x +5x +5x +5x +5x +  +5x +  +5x +5x +  +  +  +  +  +  +  +  +  +  +5x +  +  +5x +5x +  +5x +5x +5x +5x +  +  +  +  +  +5x + 
console.log({ env: process.env.NODE_ENV });
+Eif (process.env.NODE_ENV !== "production") {
+  // hanya dipake ketika proses development
+  // kalo production kita tidak menggunakan library dotenv -> env bawaan dari pm2 (runner)
+  require("dotenv").config();
+}
+ 
+const express = require("express");
+const cors = require("cors");
+const app = express();
+const authRoutes = require("./routes/auth"); // nanti kita buat
+const publicRoutes = require("./routes/public");
+const courtRoutes = require("./routes/courts"); // nanti
+const bookingRoutes = require("./routes/bookings"); // nanti
+const paymentRoutes = require("./routes/payments"); // nanti
+// const errorHandler = require("./middleware/errorHandler"); // nanti
+const aiRoutes = require("./routes/ai"); // untuk AI
+ 
+app.use(cors());
+app.use(express.urlencoded({ extended: false }));
+ 
+// ⚠️ PENTING: PASANG RAW BODY HANYA UNTUK MIDTRANS CALLBACK
+// app.use(
+//   "/payments/midtrans/callback",
+//   express.raw({ type: "*/*" }) // Midtrans butuh raw body
+// );
+// app.get("/", (req, res) => {
+//   res.status(200).json({ message: "its my life" });
+// });
+ 
+app.use(express.json());
+ 
+// Routes
+app.use("/auth", authRoutes); // POST /auth/register, /login
+app.use("/public", publicRoutes);
+ 
+app.use("/courts", courtRoutes);
+app.use("/bookings", bookingRoutes);
+app.use("/payments", paymentRoutes);
+app.use("/ai", aiRoutes);
+// console.log("✅ /ai route berhasil dimount");
+// router.use(authentication); // Semua route setelah ini butuh login
+// Error handler
+// app.use(errorHandler);
+ 
+module.exports = app;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/config/config.js.html b/Server/coverage/lcov-report/Server/config/config.js.html new file mode 100644 index 0000000..7c71b50 --- /dev/null +++ b/Server/coverage/lcov-report/Server/config/config.js.html @@ -0,0 +1,169 @@ + + + + + + Code coverage report for Server/config/config.js + + + + + + + + + +
+
+

All files / Server/config config.js

+
+ +
+ 100% + Statements + 2/2 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 2/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +295x +  +5x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
require("dotenv").config();
+ 
+module.exports = {
+  development: {
+    username: process.env.DB_USER,
+    password: process.env.DB_PASS,
+    database: process.env.DB_NAME,
+    host: process.env.DB_HOST,
+    dialect: "postgres",
+  },
+  test: {
+    username: process.env.DB_USER,
+    password: process.env.DB_PASS,
+    database: process.env.DB_NAME_TEST || "sportify_test",
+    host: process.env.DB_HOST,
+    dialect: "postgres",
+  },
+  production: {
+    use_env_variable: "DATABASE_URL",
+    dialect: "postgres",
+    dialectOptions: {
+      ssl: {
+        require: true,
+        rejectUnauthorized: false,
+      },
+    },
+  },
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/config/index.html b/Server/coverage/lcov-report/Server/config/index.html new file mode 100644 index 0000000..e83e976 --- /dev/null +++ b/Server/coverage/lcov-report/Server/config/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for Server/config + + + + + + + + + +
+
+

All files Server/config

+
+ +
+ 100% + Statements + 2/2 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 2/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
config.js +
+
100%2/250%1/2100%0/0100%2/2
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/controllers/authController.js.html b/Server/coverage/lcov-report/Server/controllers/authController.js.html new file mode 100644 index 0000000..0145902 --- /dev/null +++ b/Server/coverage/lcov-report/Server/controllers/authController.js.html @@ -0,0 +1,397 @@ + + + + + + Code coverage report for Server/controllers/authController.js + + + + + + + + + +
+
+

All files / Server/controllers authController.js

+
+ +
+ 100% + Statements + 43/43 +
+ + +
+ 93.75% + Branches + 15/16 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 100% + Lines + 43/43 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +1055x +5x +5x +  +5x +5x +  +5x +  +11x +11x +  +11x +1x +  +  +  +  +  +  +  +  +  +10x +  +  +  +  +11x +11x +11x +3x +  +8x +8x +1x +  +7x +2x +  +5x +1x +  +  +  +  +4x +3x +1x +  +2x +2x +1x +  +1x +1x +  +  +  +  +  +  +  +  +  +8x +  +  +  +  +3x +3x +3x +1x +  +2x +  +  +  +2x +2x +1x +1x +  +  +  +  +  +  +1x +1x +  +  +  +  +  +  +  +  +  +2x +  +  +  + 
const { comparePassword } = require("../helpers/bcrypt");
+const { signToken } = require("../helpers/jwt");
+const { User } = require("../models");
+ 
+const { OAuth2Client } = require("google-auth-library");
+const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
+ 
+module.exports = class authController {
+  static async register(req, res, next) {
+    try {
+      const { name, email, password, role } = req.body;
+ 
+      const newUser = await User.create(req.body);
+      res.status(201).json({
+        message: "User registered successfully",
+        user: {
+          id: newUser.id,
+          name: newUser.name,
+          email: newUser.email,
+          role: newUser.role,
+        },
+      });
+    } catch (err) {
+      next(err);
+    }
+  }
+ 
+  static async login(req, res, next) {
+    const { email, password } = req.body;
+    try {
+      if (!email) {
+        throw { name: "BadRequest", message: "Email is required" }; // 400
+      }
+      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+      if (!emailRegex.test(email)) {
+        throw { name: "BadRequest", message: "Invalid email format" }; // 400
+      }
+      if (!password) {
+        throw { name: "BadRequest", message: "Password is required" }; // 400
+      }
+      if (password.length < 6) {
+        throw {
+          name: "BadRequest",
+          message: "Password must be at least 6 characters long",
+        };
+      }
+      const user = await User.findOne({ where: { email } });
+      if (!user) {
+        return res.status(404).json({ message: "User not found" });
+      }
+      const isValidPassword = comparePassword(password, user.password);
+      if (!isValidPassword) {
+        return res.status(401).json({ message: "Invalid email or password" });
+      }
+      const accessToken = signToken({ id: user.id, role: user.role });
+      res.status(200).json({
+        message: "Login Success",
+        access_token: accessToken,
+        user: {
+          id: user.id,
+          name: user.name,
+          role: user.role,
+        },
+      });
+    } catch (err) {
+      next(err);
+    }
+  }
+ 
+  static async googleLogin(req, res, next) {
+    const { id_token } = req.body;
+    try {
+      if (!id_token) {
+        throw { name: "BadRequest", message: "id_token is required" }; // 400
+      }
+      const ticket = await client.verifyIdToken({
+        idToken: id_token,
+        audience: process.env.GOOGLE_CLIENT_ID,
+      });
+      const { name, email } = ticket.getPayload();
+      let user = await User.findOne({ where: { email } });
+      Eif (!user) {
+        user = await User.create({
+          name,
+          email,
+          password: Math.random().toString(36).slice(-8),
+          role: "user",
+        });
+      }
+      const access_token = signToken({ id: user.id, role: user.role });
+      res.status(200).json({
+        message: "Google Login Success",
+        access_token,
+        user: {
+          id: user.id,
+          name: user.name,
+          role: user.role,
+        },
+      });
+    } catch (err) {
+      next(err);
+    }
+  }
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/controllers/bookingController.js.html b/Server/coverage/lcov-report/Server/controllers/bookingController.js.html new file mode 100644 index 0000000..af09b4a --- /dev/null +++ b/Server/coverage/lcov-report/Server/controllers/bookingController.js.html @@ -0,0 +1,628 @@ + + + + + + Code coverage report for Server/controllers/bookingController.js + + + + + + + + + +
+
+

All files / Server/controllers bookingController.js

+
+ +
+ 76.66% + Statements + 46/60 +
+ + +
+ 67.85% + Branches + 19/28 +
+ + +
+ 83.33% + Functions + 5/6 +
+ + +
+ 77.19% + Lines + 44/57 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +1825x +  +5x +  +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +4x +4x +4x +  +  +4x +4x +1x +  +  +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +1x +  +  +  +  +  +2x +  +  +  +  +  +  +2x +  +2x +  +  +  +  +1x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +1x +  +  +  +  +  +  +1x +  +  +  +  +  +  +2x +2x +2x +  +2x +2x +  +  +1x +1x +  +  +  +1x +  +1x +  +1x +  +  +  +  +2x +2x +2x +2x +1x +1x +  +1x +  +  +  + 
const { Booking, Court, User, Payment } = require("../models");
+ 
+module.exports = class bookingController {
+  //* Buat Booking (user)
+  static async getMyBookings(req, res, next) {
+    try {
+      const userId = req.user.id;
+      // console.log("🚀 ~ getMyBookings ~ userId:", userId);
+      const bookings = await Booking.findAll({
+        where: { UserId: userId },
+        include: [
+          Court,
+          {
+            model: Payment,
+            attributes: ["status", "paidAt"],
+            required: false,
+          },
+        ],
+      });
+      res.status(200).json(bookings);
+    } catch (err) {
+      // console.log("🚀 ~ getMyBookings ~ err:", err);
+      next(err);
+    }
+  }
+  //* Ambil semua booking (admin)
+  static async getAllBookings(req, res, next) {
+    try {
+      const { status } = req.query;
+ 
+      const options = {
+        include: [
+          { model: Court },
+          {
+            model: User,
+            attributes: ["id", "email", "name"],
+          },
+        ],
+        order: [["createdAt", "DESC"]],
+      };
+ 
+      // Tambahkan filter jika ada query status
+      if (status) {
+        options.where = { status };
+        // console.log("🚀 ~ getAllBookings ~ options:", options);
+      }
+ 
+      const bookings = await Booking.findAll(options);
+      res.status(200).json(bookings);
+    } catch (err) {
+      next(err); // pastikan error dikirim ke error handler
+    }
+  }
+  //* Create Booking (user)
+  static async createBooking(req, res, next) {
+    try {
+      const userId = req.user.id;
+      const { CourtId, date, timeStart, timeEnd } = req.body;
+ 
+      // Check if court exists
+      const court = await Court.findByPk(CourtId);
+      if (!court) {
+        throw { name: "NotFound", message: "Court not found" };
+      }
+ 
+      // Check for overlapping bookings
+      const existingBooking = await Booking.findOne({
+        where: {
+          CourtId,
+          date,
+          status: { [require("sequelize").Op.ne]: "cancelled" }, // exclude cancelled bookings
+          [require("sequelize").Op.or]: [
+            // New booking starts during existing booking
+            {
+              timeStart: { [require("sequelize").Op.lte]: timeStart },
+              timeEnd: { [require("sequelize").Op.gt]: timeStart },
+            },
+            // New booking ends during existing booking
+            {
+              timeStart: { [require("sequelize").Op.lt]: timeEnd },
+              timeEnd: { [require("sequelize").Op.gte]: timeEnd },
+            },
+            // New booking completely contains existing booking
+            {
+              timeStart: { [require("sequelize").Op.gte]: timeStart },
+              timeEnd: { [require("sequelize").Op.lte]: timeEnd },
+            },
+          ],
+        },
+      });
+ 
+      if (existingBooking) {
+        throw {
+          name: "BadRequest",
+          message: "Booking time overlaps with existing booking",
+        };
+      }
+ 
+      const booking = await Booking.create({
+        CourtId,
+        UserId: userId,
+        date,
+        timeStart,
+        timeEnd,
+      });
+      res.status(201).json({ message: "Booking created", booking });
+    } catch (err) {
+      next(err);
+    }
+  }
+  //* Update Booking (user can update their own, admin can update any)
+  static async updateBooking(req, res, next) {
+    try {
+      const { id } = req.params;
+      const { date, timeStart, timeEnd, status } = req.body;
+      const booking = await Booking.findByPk(id);
+      Iif (!booking) throw { name: "NotFound", message: "Booking not found" };
+ 
+      // Check if user owns this booking (unless admin)
+      Iif (req.user.role !== "admin" && booking.UserId !== req.user.id) {
+        throw {
+          name: "Forbidden",
+          message: "You can only update your own bookings",
+        };
+      }
+ 
+      //* Validasi hanya boleh update booking dengan status pending
+      Iif (booking.status !== "pending") {
+        throw {
+          name: "Forbidden",
+          message: "Only bookings with status 'pending' can be updated",
+        };
+      }
+      //* Update hanya field tertentu
+      await booking.update({
+        date: date || booking.date,
+        timeStart: timeStart || booking.timeStart,
+        timeEnd: timeEnd || booking.timeEnd,
+        status: status || booking.status,
+      });
+ 
+      res.status(200).json({ message: "Booking updated" });
+    } catch (err) {
+      next(err);
+    }
+  }
+  //* Update Booking Status (admin)
+  static async updateBookingStatus(req, res, next) {
+    try {
+      const { id } = req.params;
+      const { status } = req.body;
+ 
+      const booking = await Booking.findByPk(id);
+      if (!booking) throw { name: "NotFound", message: "Booking not found" };
+ 
+      // Validasi status (opsional, hanya allow certain status)
+      const validStatuses = ["pending", "approved", "cancelled"];
+      Iif (!validStatuses.includes(status)) {
+        throw { name: "BadRequest", message: "Invalid status value" };
+      }
+ 
+      await booking.update({ status });
+ 
+      res.status(200).json({ message: "Booking status updated", booking });
+    } catch (err) {
+      next(err);
+    }
+  }
+  //* Hapus Booking (admin)
+  static async deleteBooking(req, res, next) {
+    try {
+      const { id } = req.params;
+      const booking = await Booking.findByPk(id);
+      if (!booking) throw { name: "NotFound", message: "Booking not found" };
+      await booking.destroy();
+      res.status(200).json({ message: "Booking deleted" });
+    } catch (err) {
+      next(err);
+    }
+  }
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/controllers/courtController.js.html b/Server/coverage/lcov-report/Server/controllers/courtController.js.html new file mode 100644 index 0000000..22231ab --- /dev/null +++ b/Server/coverage/lcov-report/Server/controllers/courtController.js.html @@ -0,0 +1,337 @@ + + + + + + Code coverage report for Server/controllers/courtController.js + + + + + + + + + +
+
+

All files / Server/controllers courtController.js

+
+ +
+ 97.22% + Statements + 35/36 +
+ + +
+ 100% + Branches + 7/7 +
+ + +
+ 100% + Functions + 5/5 +
+ + +
+ 97.22% + Lines + 35/36 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +855x +  +5x +  +2x +2x +  +2x +  +  +2x +  +  +  +  +  +  +2x +2x +2x +2x +1x +  +1x +  +1x +  +  +  +  +2x +  +2x +2x +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +2x +2x +  +2x +2x +2x +1x +  +1x +  +  +  +  +  +  +  +1x +  +1x +  +  +  +  +2x +2x +2x +2x +1x +  +1x +1x +  +1x +  +  +  + 
const { Court } = require("../models");
+ 
+module.exports = class courtController {
+  static async getAllCourts(req, res, next) {
+    try {
+      const { order = "DESC" } = req.query;
+ 
+      const courts = await Court.findAll({
+        order: [["updatedAt", order.toUpperCase()]], // ASC / DESC
+      });
+      res.status(200).json(courts);
+    } catch (err) {
+      next(err);
+    }
+  }
+ 
+  static async getCourtById(req, res, next) {
+    try {
+      const courtId = req.params.id;
+      const court = await Court.findByPk(courtId);
+      if (!court) {
+        throw { name: "NotFound", message: "Court not found" };
+      }
+      res.status(200).json(court);
+    } catch (err) {
+      next(err);
+    }
+  }
+ 
+  static async createCourt(req, res, next) {
+    try {
+      const { name, category, location, pricePerHour, description, imageUrl } =
+        req.body;
+      const newCourt = await Court.create({
+        name,
+        category,
+        location,
+        pricePerHour,
+        description,
+        imageUrl,
+      });
+      res.status(201).json(newCourt);
+    } catch (err) {
+      next(err);
+    }
+  }
+ 
+  static async updateCourt(req, res, next) {
+    try {
+      const courtId = req.params.id;
+      const { name, category, location, pricePerHour, description, imageUrl } =
+        req.body;
+      const court = await Court.findByPk(courtId);
+      if (!court) {
+        throw { name: "NotFound", message: "Court not found" };
+      }
+      await court.update({
+        name,
+        category,
+        location,
+        pricePerHour,
+        description,
+        imageUrl,
+      });
+      res.status(200).json({ message: "Court updated successfully" });
+    } catch (err) {
+      next(err);
+    }
+  }
+ 
+  static async deleteCourt(req, res, next) {
+    try {
+      const courtId = req.params.id;
+      const court = await Court.findByPk(courtId);
+      if (!court) {
+        throw { name: "NotFound", message: "Court not found" };
+      }
+      await court.destroy();
+      res.status(200).json({ message: "Court deleted successfully" });
+    } catch (err) {
+      next(err);
+    }
+  }
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/controllers/index.html b/Server/coverage/lcov-report/Server/controllers/index.html new file mode 100644 index 0000000..a7529a4 --- /dev/null +++ b/Server/coverage/lcov-report/Server/controllers/index.html @@ -0,0 +1,176 @@ + + + + + + Code coverage report for Server/controllers + + + + + + + + + +
+
+

All files Server/controllers

+
+ +
+ 85.77% + Statements + 193/225 +
+ + +
+ 74.41% + Branches + 64/86 +
+ + +
+ 94.44% + Functions + 17/18 +
+ + +
+ 86.03% + Lines + 191/222 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
authController.js +
+
100%43/4393.75%15/16100%3/3100%43/43
bookingController.js +
+
76.66%46/6067.85%19/2883.33%5/677.19%44/57
courtController.js +
+
97.22%35/36100%7/7100%5/597.22%35/36
paymentController.js +
+
75.38%49/6550%12/24100%2/275.38%49/65
publicController.js +
+
95.23%20/21100%11/11100%2/295.23%20/21
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/controllers/paymentController.js.html b/Server/coverage/lcov-report/Server/controllers/paymentController.js.html new file mode 100644 index 0000000..1fc3416 --- /dev/null +++ b/Server/coverage/lcov-report/Server/controllers/paymentController.js.html @@ -0,0 +1,565 @@ + + + + + + Code coverage report for Server/controllers/paymentController.js + + + + + + + + + +
+
+

All files / Server/controllers paymentController.js

+
+ +
+ 75.38% + Statements + 49/65 +
+ + +
+ 50% + Branches + 12/24 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 75.38% + Lines + 49/65 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +1615x +5x +5x +5x +5x +5x +  +5x +  +1x +1x +1x +  +1x +  +  +  +  +  +1x +1x +  +  +  +  +  +1x +1x +1x +  +  +1x +1x +1x +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +  +1x +  +  +  +1x +1x +  +  +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +1x +1x +  +1x +  +  +  +  +  +  +  +  +  +1x +  +  +1x +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +1x +1x +  +  +1x +  +  +  +  +1x +  +  +1x +1x +1x +  +1x +  +  +  +1x +1x +1x +1x +  +  +  +  +  +  +  +1x +  +1x +  +1x +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  + 
const { Payment, Booking, Court } = require("../models");
+const midtransClient = require("midtrans-client");
+const { signToken } = require("../helpers/jwt");
+const { Op, or } = require("sequelize");
+const dayjs = require("dayjs");
+const axios = require("axios");
+ 
+module.exports = class paymentController {
+  static async initiateMidtransTrx(req, res, next) {
+    try {
+      console.log("Request Headers:", req.headers);
+      console.log("Request Body:", req.body);
+ 
+      Iif (!process.env.MIDTRANS_SERVER_KEY) {
+        throw {
+          name: "ServerError",
+          message: "MIDTRANS_SERVER_KEY is missing",
+        };
+      }
+      console.log("MIDTRANS_SERVER_KEY:", process.env.MIDTRANS_SERVER_KEY);
+      let snap = new midtransClient.Snap({
+        // Set to true if you want Production Environment (accept real transaction).
+        isProduction: false,
+        serverKey: process.env.MIDTRANS_SERVER_KEY,
+        clientKey: process.env.MIDTRANS_CLIENT_KEY,
+      });
+      console.log("Request Body:", req.body);
+      const { BookingId } = req.body;
+      Iif (!BookingId) {
+        throw { name: "InvalidInput", message: "BookingId is required" };
+      }
+      console.log("User ID:", req.user.id);
+      console.log("Booking ID:", req.body.BookingId);
+      Iif (!req.body.BookingId) {
+        return res.status(400).json({ message: "BookingId is required" });
+      }
+      Iif (!req.user) {
+        return res
+          .status(401)
+          .json({ message: "Unauthorized: user not found" });
+      }
+      // Cari booking berdasarkan BookingId dan UserId
+      const booking = await Booking.findOne({
+        where: {
+          id: BookingId,
+          UserId: req.user.id,
+        },
+      });
+ 
+      Iif (!booking) {
+        return res.status(404).json({ message: "Booking not found" });
+      }
+ 
+      const orderId = `BOOK-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
+      Iif (!orderId) {
+        return res.status(500).json({ message: "Order ID generation failed" });
+      }
+      const amount = 10000;
+      let parameter = {
+        // ini adalah data detail order
+        transaction_details: {
+          order_id: orderId,
+          gross_amount: amount,
+        },
+        // ini adalah data jenis pembayaran
+        credit_card: {
+          secure: true,
+        },
+        // ini adalah data customer
+        customer_details: {
+          first_name: req.user.name,
+          email: req.user.email,
+        },
+      };
+      let transaction;
+      try {
+        // 1.create transaction to midtrans
+        transaction = await snap.createTransaction(parameter);
+        console.log("📦 Midtrans Transaction Response:", transaction);
+        // transaction token
+        Iif (!transaction.token) {
+          throw new Error(
+            "Transaction token is missing from Midtrans response"
+          );
+        }
+        // console.log("transactionToken:", transactionToken);
+      } catch (err) {
+        console.error("Midtrans Error:", err.response?.data || err.message);
+        throw err;
+      }
+      let transactionToken = transaction.token;
+      // 2. create order in our database
+ 
+      await Payment.create({
+        BookingId: booking.id,
+        amount,
+        orderId,
+      });
+ 
+      res.json({ message: "Order created", transactionToken, orderId });
+    } catch (err) {
+      console.log("❌ INITIATE MIDTRANS TRX ERROR:");
+      console.dir(err, { depth: null });
+      next(err);
+    }
+  }
+ 
+  static async upgradeAccount(req, res, next) {
+    // check orderId, order ke midtrans, apakah sudah dibayar atau belum
+    try {
+      const { orderId } = req.body;
+ 
+      // Cek apakah user sudah pernah upgrade
+      const order = await Payment.findOne({
+        where: {
+          orderId,
+        },
+      });
+      Iif (!order) {
+        return res.status(404).json({ message: "Order not found" });
+      }
+      console.log("🔍 Order:", order);
+      console.log("Headers:", req.headers);
+      console.log("Authorization Header:", req.headers.authorization);
+ 
+      Iif (order.status === "paid") {
+        return res.status(400).json({ message: "Account already upgraded" });
+      }
+ 
+      const serverKey = process.env.MIDTRANS_SERVER_KEY;
+      const base64ServerKey = Buffer.from(serverKey + ":").toString("base64");
+      console.log(`Authorization: Basic ${base64ServerKey}`);
+      const { data } = await axios.get(
+        `https://api.sandbox.midtrans.com/v2/${orderId}/status`,
+        {
+          headers: {
+            Authorization: `Basic ${base64ServerKey}`,
+          },
+        }
+      );
+      console.log("📦 Midtrans Response:", data);
+      // midtrans validasi status pembayaran
+      if (data.transaction_status === "capture" && data.status_code === "200") {
+        // Update status pembayaran di database
+        await order.update({
+          status: "paid",
+          paidAt: new Date(),
+        });
+        res.status(200).json({ message: "Account upgraded successfully" });
+      } else E{
+        return res.status(400).json({
+          message: "Payment not completed or invalid transaction status",
+          midtransMessage: data.status_message,
+        });
+      }
+    } catch (err) {
+      next(err);
+    }
+  }
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/controllers/publicController.js.html b/Server/coverage/lcov-report/Server/controllers/publicController.js.html new file mode 100644 index 0000000..1850d07 --- /dev/null +++ b/Server/coverage/lcov-report/Server/controllers/publicController.js.html @@ -0,0 +1,241 @@ + + + + + + Code coverage report for Server/controllers/publicController.js + + + + + + + + + +
+
+

All files / Server/controllers publicController.js

+
+ +
+ 95.23% + Statements + 20/21 +
+ + +
+ 100% + Branches + 11/11 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 95.23% + Lines + 20/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +535x +5x +  +5x +  +3x +3x +3x +  +3x +1x +  +  +  +  +  +3x +1x +  +  +3x +3x +  +  +  +  +3x +  +  +  +  +  +  +  +  +  +  +  +  +  +2x +2x +2x +2x +1x +  +1x +  +1x +  +  +  + 
const { Court } = require("../models");
+const { Op } = require("sequelize");
+ 
+module.exports = class publicController {
+  static async getCourts(req, res, next) {
+    try {
+      const { search = "", category, page = 1, limit = 10 } = req.query;
+      const where = {};
+      // Search by name or location (case-insensitive)
+      if (search && !category) {
+        where[Op.or] = [
+          { name: { [Op.iLike]: `%${search}%` } },
+          { location: { [Op.iLike]: `%${search}%` } },
+        ];
+      }
+      // Filter by category
+      if (category) {
+        where.category = { [Op.iLike]: `%${category}%` };
+      }
+      // console.log("FILTER CONDITIONS:", { where });
+      const offset = (page - 1) * limit;
+      const { rows, count } = await Court.findAndCountAll({
+        where,
+        limit: +limit,
+        offset: +offset,
+      });
+      res.status(200).json({
+        data: rows,
+        pagination: {
+          page: +page,
+          totalPages: Math.ceil(count / limit),
+          totalData: count,
+        },
+      });
+    } catch (err) {
+      next(err);
+    }
+  }
+ 
+  static async getCourtsById(req, res, next) {
+    try {
+      const courtId = req.params.id;
+      const court = await Court.findByPk(courtId);
+      if (!court) {
+        throw { name: "NotFound", message: "Court not found" };
+      }
+      res.status(200).json(court);
+    } catch (err) {
+      next(err);
+    }
+  }
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/helpers/AskGemini.js.html b/Server/coverage/lcov-report/Server/helpers/AskGemini.js.html new file mode 100644 index 0000000..0821901 --- /dev/null +++ b/Server/coverage/lcov-report/Server/helpers/AskGemini.js.html @@ -0,0 +1,133 @@ + + + + + + Code coverage report for Server/helpers/AskGemini.js + + + + + + + + + +
+
+

All files / Server/helpers AskGemini.js

+
+ +
+ 100% + Statements + 9/9 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 9/9 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +176x +6x +  +6x +  +  +2x +  +2x +  +2x +2x +2x +  +  +6x + 
const { GoogleGenerativeAI } = require("@google/generative-ai");
+require("dotenv").config();
+ 
+const genAI = new GoogleGenerativeAI(process.env.GOOGLE_GENAI_API_KEY);
+ 
+async function askGemini(prompt) {
+  console.log("🔥 askGemini DIPANGGIL");
+ 
+  const model = genAI.getGenerativeModel({ model: "models/gemini-1.5-flash" });
+ 
+  const result = await model.generateContent(prompt);
+  const response = await result.response;
+  return response.text();
+}
+ 
+module.exports = { askGemini };
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/helpers/bcrypt.js.html b/Server/coverage/lcov-report/Server/helpers/bcrypt.js.html new file mode 100644 index 0000000..fa73da4 --- /dev/null +++ b/Server/coverage/lcov-report/Server/helpers/bcrypt.js.html @@ -0,0 +1,130 @@ + + + + + + Code coverage report for Server/helpers/bcrypt.js + + + + + + + + + +
+
+

All files / Server/helpers bcrypt.js

+
+ +
+ 100% + Statements + 7/7 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 7/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +165x +  +5x +7x +7x +  +  +5x +2x +  +  +5x +  +  +  + 
const bcrypt = require("bcryptjs");
+ 
+const hashPassword = (password) => {
+  const salt = bcrypt.genSaltSync(10);
+  return bcrypt.hashSync(password, salt);
+};
+ 
+const comparePassword = (plainPassword, hashedPassword) => {
+  return bcrypt.compareSync(plainPassword, hashedPassword);
+};
+ 
+module.exports = {
+  hashPassword,
+  comparePassword,
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/helpers/index.html b/Server/coverage/lcov-report/Server/helpers/index.html new file mode 100644 index 0000000..4005af2 --- /dev/null +++ b/Server/coverage/lcov-report/Server/helpers/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for Server/helpers + + + + + + + + + +
+
+

All files Server/helpers

+
+ +
+ 100% + Statements + 21/21 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 100% + Functions + 5/5 +
+ + +
+ 100% + Lines + 21/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
AskGemini.js +
+
100%9/9100%0/0100%1/1100%9/9
bcrypt.js +
+
100%7/7100%0/0100%2/2100%7/7
jwt.js +
+
100%5/550%1/2100%2/2100%5/5
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/helpers/jwt.js.html b/Server/coverage/lcov-report/Server/helpers/jwt.js.html new file mode 100644 index 0000000..54dbf4d --- /dev/null +++ b/Server/coverage/lcov-report/Server/helpers/jwt.js.html @@ -0,0 +1,121 @@ + + + + + + Code coverage report for Server/helpers/jwt.js + + + + + + + + + +
+
+

All files / Server/helpers jwt.js

+
+ +
+ 100% + Statements + 5/5 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 5/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +135x +5x +  +  +6x +  +  +  +23x +  +  +5x + 
const jwt = require("jsonwebtoken");
+const SECRET = process.env.JWT_SECRET || "sportify123";
+ 
+function signToken(payload) {
+  return jwt.sign(payload, SECRET);
+}
+ 
+function verifyToken(token) {
+  return jwt.verify(token, SECRET);
+}
+ 
+module.exports = { signToken, verifyToken };
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/index.html b/Server/coverage/lcov-report/Server/index.html new file mode 100644 index 0000000..b40ac6c --- /dev/null +++ b/Server/coverage/lcov-report/Server/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for Server + + + + + + + + + +
+
+

All files Server

+
+ +
+ 100% + Statements + 22/22 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 22/22 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
app.js +
+
100%22/2250%1/2100%0/0100%22/22
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/middleware/authentication.js.html b/Server/coverage/lcov-report/Server/middleware/authentication.js.html new file mode 100644 index 0000000..ecec621 --- /dev/null +++ b/Server/coverage/lcov-report/Server/middleware/authentication.js.html @@ -0,0 +1,181 @@ + + + + + + Code coverage report for Server/middleware/authentication.js + + + + + + + + + +
+
+

All files / Server/middleware authentication.js

+
+ +
+ 88.88% + Statements + 16/18 +
+ + +
+ 75% + Branches + 3/4 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 88.88% + Lines + 16/18 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +335x +5x +  +  +5x +  +25x +25x +25x +25x +2x +2x +  +23x +  +23x +  +23x +  +23x +  +23x +  +  +  +23x +  +23x +  +  +  +  + 
const { User } = require("../models");
+const { verifyToken } = require("../helpers/jwt");
+ 
+//! authentication = untuk identify user/verifikasi(dia itu siapa?)
+module.exports = async function authentication(req, res, next) {
+  //! Extract token: Get Bearer token from authorization header
+  console.log("Headers:", req.headers);
+  const bearerToken = req.headers.authorization;
+  console.log("🔐 Authorization Header:", bearerToken);
+  if (!bearerToken) {
+    next({ name: "Unauthorized", message: "Invalid token" }); //401
+    return;
+  }
+  const access_token = bearerToken.split(" ")[1];
+ 
+  try {
+    //! Verify token
+    const data = verifyToken(access_token);
+ 
+    const user = await User.findByPk(data.id);
+ 
+    Iif (!user) {
+      throw { name: "Unauthorized", message: "Invalid token" }; //401
+    }
+    //! Attach user
+    req.user = user;
+    //! continue: call next() to proceed
+    next();
+  } catch (err) {
+    next(err);
+  }
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/middleware/errorHandler.js.html b/Server/coverage/lcov-report/Server/middleware/errorHandler.js.html new file mode 100644 index 0000000..324f290 --- /dev/null +++ b/Server/coverage/lcov-report/Server/middleware/errorHandler.js.html @@ -0,0 +1,136 @@ + + + + + + Code coverage report for Server/middleware/errorHandler.js + + + + + + + + + +
+
+

All files / Server/middleware errorHandler.js

+
+ +
+ 92.85% + Statements + 13/14 +
+ + +
+ 91.66% + Branches + 11/12 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 92.85% + Lines + 13/14 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +186x +33x +9x +24x +10x +14x +2x +12x +  +12x +1x +11x +7x +  +4x +  +  + 
module.exports = function errorHandler(err, req, res, next) {
+  if (err.name === "BadRequest") {
+    res.status(400).json({ message: err.message });
+  } else if (err.name === "SequelizeValidationError") {
+    res.status(400).json({ message: err.errors[0].message });
+  } else if (err.name === "Unauthorized") {
+    res.status(401).json({ message: err.message });
+  } else Iif (err.name === "JsonWebTokenError") {
+    res.status(401).json({ message: "Invalid token" });
+  } else if (err.name === "Forbidden") {
+    res.status(403).json({ message: err.message });
+  } else if (err.name === "NotFound") {
+    res.status(404).json({ message: err.message });
+  } else {
+    res.status(500).json({ message: "Internal Server Error" });
+  }
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/middleware/index.html b/Server/coverage/lcov-report/Server/middleware/index.html new file mode 100644 index 0000000..43e3342 --- /dev/null +++ b/Server/coverage/lcov-report/Server/middleware/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for Server/middleware + + + + + + + + + +
+
+

All files Server/middleware

+
+ +
+ 91.66% + Statements + 33/36 +
+ + +
+ 88.88% + Branches + 16/18 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 91.66% + Lines + 33/36 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
authentication.js +
+
88.88%16/1875%3/4100%1/188.88%16/18
errorHandler.js +
+
92.85%13/1491.66%11/12100%1/192.85%13/14
isAdmin.js +
+
100%4/4100%2/2100%1/1100%4/4
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/middleware/isAdmin.js.html b/Server/coverage/lcov-report/Server/middleware/isAdmin.js.html new file mode 100644 index 0000000..4dda1ba --- /dev/null +++ b/Server/coverage/lcov-report/Server/middleware/isAdmin.js.html @@ -0,0 +1,112 @@ + + + + + + Code coverage report for Server/middleware/isAdmin.js + + + + + + + + + +
+
+

All files / Server/middleware isAdmin.js

+
+ +
+ 100% + Statements + 4/4 +
+ + +
+ 100% + Branches + 2/2 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 4/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +105x +15x +1x +  +  +  +  +14x +  + 
module.exports = function authorizeAdmin(req, res, next) {
+  if (req.user.role !== "admin") {
+    return next({
+      name: "Forbidden",
+      message: "Only admin can access this resource",
+    }); // 403
+  }
+  next();
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/models/booking.js.html b/Server/coverage/lcov-report/Server/models/booking.js.html new file mode 100644 index 0000000..2a35da6 --- /dev/null +++ b/Server/coverage/lcov-report/Server/models/booking.js.html @@ -0,0 +1,220 @@ + + + + + + Code coverage report for Server/models/booking.js + + + + + + + + + +
+
+

All files / Server/models booking.js

+
+ +
+ 100% + Statements + 7/7 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 7/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46  +5x +5x +  +  +  +  +  +  +  +  +5x +5x +5x +  +  +5x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +5x +  + 
"use strict";
+const { Model } = require("sequelize");
+module.exports = (sequelize, DataTypes) => {
+  class Booking extends Model {
+    /**
+     * Helper method for defining associations.
+     * This method is not a part of Sequelize lifecycle.
+     * The `models/index` file will call this method automatically.
+     */
+    static associate(models) {
+      // define association here
+      Booking.belongsTo(models.User, { foreignKey: "UserId" });
+      Booking.belongsTo(models.Court, { foreignKey: "CourtId" });
+      Booking.hasOne(models.Payment, { foreignKey: "BookingId" });
+    }
+  }
+  Booking.init(
+    {
+      UserId: {
+        type: DataTypes.INTEGER,
+        allowNull: false,
+      },
+      CourtId: {
+        type: DataTypes.INTEGER,
+        allowNull: false,
+      },
+      date: DataTypes.DATEONLY,
+      timeStart: DataTypes.TIME,
+      timeEnd: DataTypes.TIME,
+      status: {
+        type: DataTypes.STRING,
+        defaultValue: "pending",
+      },
+      isPaid: {
+        type: DataTypes.BOOLEAN,
+        defaultValue: false,
+      },
+    },
+    {
+      sequelize,
+      modelName: "Booking",
+    }
+  );
+  return Booking;
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/models/court.js.html b/Server/coverage/lcov-report/Server/models/court.js.html new file mode 100644 index 0000000..a7c1d0e --- /dev/null +++ b/Server/coverage/lcov-report/Server/models/court.js.html @@ -0,0 +1,373 @@ + + + + + + Code coverage report for Server/models/court.js + + + + + + + + + +
+
+

All files / Server/models court.js

+
+ +
+ 100% + Statements + 5/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 5/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97  +5x +5x +  +  +  +  +  +  +  +  +5x +  +  +5x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +5x +  + 
"use strict";
+const { Model } = require("sequelize");
+module.exports = (sequelize, DataTypes) => {
+  class Court extends Model {
+    /**
+     * Helper method for defining associations.
+     * This method is not a part of Sequelize lifecycle.
+     * The `models/index` file will call this method automatically.
+     */
+    static associate(models) {
+      // define association here
+      Court.hasMany(models.Booking, { foreignKey: "CourtId" });
+    }
+  }
+  Court.init(
+    {
+      name: {
+        type: DataTypes.STRING,
+        allowNull: false,
+        validate: {
+          notNull: {
+            msg: "Name is required",
+          },
+          notEmpty: {
+            msg: "Name is required",
+          },
+        },
+      },
+      category: {
+        type: DataTypes.STRING,
+        allowNull: false,
+        validate: {
+          notNull: {
+            msg: "Category is required",
+          },
+          notEmpty: {
+            msg: "Category is required",
+          },
+        },
+      },
+      location: {
+        type: DataTypes.STRING,
+        allowNull: false,
+        validate: {
+          notNull: {
+            msg: "Location is required",
+          },
+          notEmpty: {
+            msg: "Location is required",
+          },
+        },
+      },
+      pricePerHour: {
+        type: DataTypes.INTEGER,
+        allowNull: false,
+        validate: {
+          notNull: {
+            msg: "Price is required",
+          },
+          notEmpty: {
+            msg: "Price is required",
+          },
+        },
+      },
+      description: {
+        type: DataTypes.TEXT,
+        allowNull: false,
+        validate: {
+          notNull: {
+            msg: "Description is required",
+          },
+          notEmpty: {
+            msg: "Description is required",
+          },
+        },
+      },
+      imageUrl: {
+        type: DataTypes.STRING,
+        allowNull: false,
+        validate: {
+          notNull: {
+            msg: "Image URL is required",
+          },
+          notEmpty: {
+            msg: "Image URL is required",
+          },
+        },
+      },
+    },
+    {
+      sequelize,
+      modelName: "Court",
+    }
+  );
+  return Court;
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/models/index.html b/Server/coverage/lcov-report/Server/models/index.html new file mode 100644 index 0000000..4fc937e --- /dev/null +++ b/Server/coverage/lcov-report/Server/models/index.html @@ -0,0 +1,176 @@ + + + + + + Code coverage report for Server/models + + + + + + + + + +
+
+

All files Server/models

+
+ +
+ 97.82% + Statements + 45/46 +
+ + +
+ 70% + Branches + 7/10 +
+ + +
+ 100% + Functions + 12/12 +
+ + +
+ 97.82% + Lines + 45/46 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
booking.js +
+
100%7/7100%0/0100%2/2100%7/7
court.js +
+
100%5/5100%0/0100%2/2100%5/5
index.js +
+
95.23%20/2170%7/10100%3/395.23%20/21
payment.js +
+
100%5/5100%0/0100%2/2100%5/5
user.js +
+
100%8/8100%0/0100%3/3100%8/8
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/models/index.js.html b/Server/coverage/lcov-report/Server/models/index.js.html new file mode 100644 index 0000000..5ae3aef --- /dev/null +++ b/Server/coverage/lcov-report/Server/models/index.js.html @@ -0,0 +1,235 @@ + + + + + + Code coverage report for Server/models/index.js + + + + + + + + + +
+
+

All files / Server/models index.js

+
+ +
+ 95.23% + Statements + 20/21 +
+ + +
+ 70% + Branches + 7/10 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 95.23% + Lines + 20/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51  +  +5x +5x +5x +5x +5x +5x +5x +5x +  +  +5x +  +  +5x +  +  +  +  +  +  +  +5x +  +25x +  +  +  +  +  +  +  +20x +  +  +  +20x +  +  +5x +20x +20x +  +  +  +5x +5x +  +5x + 
"use strict";
+ 
+const fs = require("fs");
+const path = require("path");
+const Sequelize = require("sequelize");
+const process = require("process");
+const basename = path.basename(__filename);
+const env = process.env.NODE_ENV || "development";
+const config = require(__dirname + "/../config/config.js")[env];
+const db = {};
+ 
+let sequelize;
+Iif (config.use_env_variable) {
+  sequelize = new Sequelize(process.env[config.use_env_variable], config);
+} else {
+  sequelize = new Sequelize(
+    config.database,
+    config.username,
+    config.password,
+    config
+  );
+}
+ 
+fs.readdirSync(__dirname)
+  .filter((file) => {
+    return (
+      file.indexOf(".") !== 0 &&
+      file !== basename &&
+      file.slice(-3) === ".js" &&
+      file.indexOf(".test.js") === -1
+    );
+  })
+  .forEach((file) => {
+    const model = require(path.join(__dirname, file))(
+      sequelize,
+      Sequelize.DataTypes
+    );
+    db[model.name] = model;
+  });
+ 
+Object.keys(db).forEach((modelName) => {
+  Eif (db[modelName].associate) {
+    db[modelName].associate(db);
+  }
+});
+ 
+db.sequelize = sequelize;
+db.Sequelize = Sequelize;
+ 
+module.exports = db;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/models/payment.js.html b/Server/coverage/lcov-report/Server/models/payment.js.html new file mode 100644 index 0000000..083363f --- /dev/null +++ b/Server/coverage/lcov-report/Server/models/payment.js.html @@ -0,0 +1,301 @@ + + + + + + Code coverage report for Server/models/payment.js + + + + + + + + + +
+
+

All files / Server/models payment.js

+
+ +
+ 100% + Statements + 5/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 5/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73  +5x +5x +  +  +  +5x +  +  +5x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +5x +  + 
"use strict";
+const { Model } = require("sequelize");
+module.exports = (sequelize, DataTypes) => {
+  class Payment extends Model {
+    static associate(models) {
+      // define association here
+      Payment.belongsTo(models.Booking, { foreignKey: "BookingId" });
+    }
+  }
+  Payment.init(
+    {
+      BookingId: {
+        type: DataTypes.INTEGER,
+        allowNull: false,
+        validate: {
+          notNull: { msg: "BookingId is required" },
+          isInt: { msg: "BookingId must be an integer" },
+        },
+      },
+      amount: {
+        type: DataTypes.INTEGER,
+        allowNull: false,
+        validate: {
+          notNull: { msg: "Amount is required" },
+          min: { args: [1], msg: "Amount must be at least 1" },
+          isInt: { msg: "Amount must be a number" },
+        },
+      },
+      method: {
+        type: DataTypes.STRING,
+        defaultValue: "midtrans",
+      },
+      status: {
+        type: DataTypes.STRING,
+        allowNull: false,
+        defaultValue: "pending",
+        validate: {
+          isIn: {
+            args: [["unpaid", "pending", "paid", "failed"]],
+            msg: "Status must be one of: unpaid, pending, paid, failed",
+          },
+        },
+      },
+      paymentUrl: {
+        type: DataTypes.STRING,
+        validate: {
+          isUrl: { msg: "paymentUrl must be a valid URL" },
+        },
+      },
+      paidAt: {
+        type: DataTypes.DATE,
+      },
+      orderId: {
+        type: DataTypes.STRING,
+        allowNull: false,
+        validate: {
+          notNull: {
+            msg: "OrderId is required",
+          },
+          notEmpty: {
+            msg: "OrderId cannot be empty",
+          },
+        },
+      },
+    },
+    {
+      sequelize,
+      modelName: "Payment",
+    }
+  );
+  return Payment;
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/models/user.js.html b/Server/coverage/lcov-report/Server/models/user.js.html new file mode 100644 index 0000000..d2bd113 --- /dev/null +++ b/Server/coverage/lcov-report/Server/models/user.js.html @@ -0,0 +1,307 @@ + + + + + + Code coverage report for Server/models/user.js + + + + + + + + + +
+
+

All files / Server/models user.js

+
+ +
+ 100% + Statements + 8/8 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 100% + Lines + 8/8 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75  +5x +5x +5x +  +  +  +  +  +  +  +  +5x +  +  +5x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +5x +7x +  +5x +  + 
"use strict";
+const { Model } = require("sequelize");
+const { hashPassword } = require("../helpers/bcrypt");
+module.exports = (sequelize, DataTypes) => {
+  class User extends Model {
+    /**
+     * Helper method for defining associations.
+     * This method is not a part of Sequelize lifecycle.
+     * The `models/index` file will call this method automatically.
+     */
+    static associate(models) {
+      // define association here
+      User.hasMany(models.Booking, { foreignKey: "UserId" });
+    }
+  }
+  User.init(
+    {
+      name: {
+        type: DataTypes.STRING,
+        allowNull: false,
+        validate: {
+          notEmpty: {
+            msg: "Name cannot be empty",
+          },
+          notNull: {
+            msg: "Name is required",
+          },
+        },
+      },
+      email: {
+        type: DataTypes.STRING,
+        allowNull: false,
+        unique: true,
+        validate: {
+          isEmail: true,
+          notEmpty: {
+            msg: "Email cannot be empty",
+          },
+          notNull: {
+            msg: "Email is required",
+          },
+        },
+      },
+      password: {
+        type: DataTypes.STRING,
+        allowNull: false,
+        validate: {
+          notEmpty: {
+            msg: "Password cannot be empty",
+          },
+          notNull: {
+            msg: "Password is required",
+          },
+          len: {
+            args: [6],
+            msg: "Password must be at least 6 characters long",
+          },
+        },
+      },
+      role: {
+        type: DataTypes.STRING,
+        defaultValue: "user",
+      },
+    },
+    {
+      sequelize,
+      modelName: "User",
+    }
+  );
+  User.beforeCreate((user) => {
+    user.password = hashPassword(user.password);
+  });
+  return User;
+};
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/routes/ai.js.html b/Server/coverage/lcov-report/Server/routes/ai.js.html new file mode 100644 index 0000000..946b824 --- /dev/null +++ b/Server/coverage/lcov-report/Server/routes/ai.js.html @@ -0,0 +1,154 @@ + + + + + + Code coverage report for Server/routes/ai.js + + + + + + + + + +
+
+

All files / Server/routes ai.js

+
+ +
+ 100% + Statements + 13/13 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 13/13 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +246x +6x +6x +  +6x +  +6x +2x +2x +2x +  +  +  +2x +1x +  +1x +  +  +  +6x +  +6x + 
const express = require("express");
+const { askGemini } = require("../helpers/AskGemini");
+const errorHandler = require("../middleware/errorHandler");
+ 
+const router = express.Router();
+ 
+router.post("/recommend", async (req, res, next) => {
+  try {
+    const { availableSlots, preference } = req.body;
+    const prompt = `Berikan rekomendasi waktu terbaik untuk booking berdasarkan preferensi berikut: ${preference}. Pilihan slot tersedia: ${availableSlots.join(
+      ", "
+    )}`;
+ 
+    const recommendation = await askGemini(prompt);
+    res.json({ recommendation });
+  } catch (err) {
+    next(err);
+  }
+});
+ 
+router.use(errorHandler);
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/routes/auth.js.html b/Server/coverage/lcov-report/Server/routes/auth.js.html new file mode 100644 index 0000000..6abe8bb --- /dev/null +++ b/Server/coverage/lcov-report/Server/routes/auth.js.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for Server/routes/auth.js + + + + + + + + + +
+
+

All files / Server/routes auth.js

+
+ +
+ 100% + Statements + 9/9 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 9/9 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +145x +5x +5x +5x +  +5x +5x +5x +  +  +5x +  +5x + 
const express = require("express");
+const router = express.Router();
+const authController = require("../controllers/authController");
+const errorHandler = require("../middleware/errorHandler");
+ 
+router.post("/login", authController.login);
+router.post("/register", authController.register);
+router.post("/login/google", authController.googleLogin);
+ 
+//* error handler
+router.use(errorHandler);
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/routes/bookings.js.html b/Server/coverage/lcov-report/Server/routes/bookings.js.html new file mode 100644 index 0000000..e828fe8 --- /dev/null +++ b/Server/coverage/lcov-report/Server/routes/bookings.js.html @@ -0,0 +1,154 @@ + + + + + + Code coverage report for Server/routes/bookings.js + + + + + + + + + +
+
+

All files / Server/routes bookings.js

+
+ +
+ 100% + Statements + 16/16 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 16/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +245x +5x +5x +5x +5x +5x +  +5x +  +5x +5x +5x +  +  +5x +5x +5x +5x +  +  +5x +  +5x + 
const express = require("express");
+const router = require("express").Router();
+const bookingController = require("../controllers/bookingController");
+const authentication = require("../middleware/authentication");
+const isAdmin = require("../middleware/isAdmin");
+const errorHandler = require("../middleware/errorHandler");
+ 
+router.use(authentication);
+// USER
+router.get("/mine", bookingController.getMyBookings);
+router.post("/", bookingController.createBooking);
+router.patch("/:id", bookingController.updateBooking); // Allow user to update their own booking
+ 
+// ADMIN
+router.get("/", isAdmin, bookingController.getAllBookings);
+router.put("/:id", isAdmin, bookingController.updateBooking);
+router.delete("/:id", isAdmin, bookingController.deleteBooking);
+router.patch("/:id/status", isAdmin, bookingController.updateBookingStatus);
+ 
+//* error handler
+router.use(errorHandler);
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/routes/courts.js.html b/Server/coverage/lcov-report/Server/routes/courts.js.html new file mode 100644 index 0000000..1aaf311 --- /dev/null +++ b/Server/coverage/lcov-report/Server/routes/courts.js.html @@ -0,0 +1,172 @@ + + + + + + Code coverage report for Server/routes/courts.js + + + + + + + + + +
+
+

All files / Server/routes courts.js

+
+ +
+ 100% + Statements + 14/14 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 14/14 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +305x +5x +5x +5x +5x +5x +  +  +5x +  +  +5x +  +  +5x +  +  +5x +  +  +5x +  +  +5x +  +  +5x +  +5x + 
const express = require("express");
+const router = express.Router();
+const courtController = require("../controllers/courtController");
+const authentication = require("../middleware/authentication");
+const isAdmin = require("../middleware/isAdmin");
+const errorHandler = require("../middleware/errorHandler");
+ 
+// Semua endpoint di sini harus login dulu
+router.use(authentication); //* Semua route setelah ini butuh login
+ 
+// GET semua lapangan (semua user bisa lihat)
+router.get("/", isAdmin, courtController.getAllCourts);
+ 
+// GET detail lapangan by id
+router.get("/:id", isAdmin, courtController.getCourtById);
+ 
+// POST buat tambah lapangan (hanya admin)
+router.post("/", isAdmin, courtController.createCourt);
+ 
+// PUT edit lapangan (admin)
+router.put("/:id", isAdmin, courtController.updateCourt);
+ 
+// DELETE hapus lapangan (admin)
+router.delete("/:id", isAdmin, courtController.deleteCourt);
+ 
+//* error handler
+router.use(errorHandler);
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/routes/index.html b/Server/coverage/lcov-report/Server/routes/index.html new file mode 100644 index 0000000..be1c820 --- /dev/null +++ b/Server/coverage/lcov-report/Server/routes/index.html @@ -0,0 +1,191 @@ + + + + + + Code coverage report for Server/routes + + + + + + + + + +
+
+

All files Server/routes

+
+ +
+ 100% + Statements + 70/70 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 70/70 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
ai.js +
+
100%13/13100%0/0100%1/1100%13/13
auth.js +
+
100%9/9100%0/0100%0/0100%9/9
bookings.js +
+
100%16/16100%0/0100%0/0100%16/16
courts.js +
+
100%14/14100%0/0100%0/0100%14/14
payments.js +
+
100%10/10100%0/0100%0/0100%10/10
public.js +
+
100%8/8100%0/0100%0/0100%8/8
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/routes/payments.js.html b/Server/coverage/lcov-report/Server/routes/payments.js.html new file mode 100644 index 0000000..23343d8 --- /dev/null +++ b/Server/coverage/lcov-report/Server/routes/payments.js.html @@ -0,0 +1,127 @@ + + + + + + Code coverage report for Server/routes/payments.js + + + + + + + + + +
+
+

All files / Server/routes payments.js

+
+ +
+ 100% + Statements + 10/10 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 10/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +155x +5x +5x +5x +5x +  +5x +  +5x +5x +  +5x +  +5x + 
const express = require("express");
+const router = express.Router();
+const authentication = require("../middleware/authentication");
+const paymentController = require("../controllers/paymentController");
+const errorHandler = require("../middleware/errorHandler");
+ 
+router.use(authentication);
+ 
+router.patch("/me/upgrade", paymentController.upgradeAccount); // hanya user yang login
+router.post("/midtrans/initiate", paymentController.initiateMidtransTrx);
+// error handler
+router.use(errorHandler);
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/Server/routes/public.js.html b/Server/coverage/lcov-report/Server/routes/public.js.html new file mode 100644 index 0000000..066a699 --- /dev/null +++ b/Server/coverage/lcov-report/Server/routes/public.js.html @@ -0,0 +1,130 @@ + + + + + + Code coverage report for Server/routes/public.js + + + + + + + + + +
+
+

All files / Server/routes public.js

+
+ +
+ 100% + Statements + 8/8 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 8/8 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +165x +5x +5x +5x +  +  +  +5x +  +5x +  +  +5x +  +5x + 
const express = require("express");
+const router = express.Router();
+const publicController = require("../controllers/publicController");
+const errorHandler = require("../middleware/errorHandler");
+ 
+//* Public routes
+// GET semua lapangan (semua user bisa lihat)
+router.get("/courts", publicController.getCourts);
+// GET detail lapangan by id
+router.get("/courts/:id", publicController.getCourtsById);
+ 
+//* error handler
+router.use(errorHandler);
+ 
+module.exports = router;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/base.css b/Server/coverage/lcov-report/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/Server/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/Server/coverage/lcov-report/block-navigation.js b/Server/coverage/lcov-report/block-navigation.js new file mode 100644 index 0000000..cc12130 --- /dev/null +++ b/Server/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/Server/coverage/lcov-report/favicon.png b/Server/coverage/lcov-report/favicon.png new file mode 100644 index 0000000..c1525b8 Binary files /dev/null and b/Server/coverage/lcov-report/favicon.png differ diff --git a/Server/coverage/lcov-report/index.html b/Server/coverage/lcov-report/index.html new file mode 100644 index 0000000..e65c34e --- /dev/null +++ b/Server/coverage/lcov-report/index.html @@ -0,0 +1,206 @@ + + + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 91.46% + Statements + 386/422 +
+ + +
+ 75% + Branches + 90/120 +
+ + +
+ 97.43% + Functions + 38/39 +
+ + +
+ 91.64% + Lines + 384/419 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
Server +
+
100%22/2250%1/2100%0/0100%22/22
Server/config +
+
100%2/250%1/2100%0/0100%2/2
Server/controllers +
+
85.77%193/22574.41%64/8694.44%17/1886.03%191/222
Server/helpers +
+
100%21/2150%1/2100%5/5100%21/21
Server/middleware +
+
91.66%33/3688.88%16/18100%3/391.66%33/36
Server/models +
+
97.82%45/4670%7/10100%12/1297.82%45/46
Server/routes +
+
100%70/70100%0/0100%1/1100%70/70
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/Server/coverage/lcov-report/prettify.css b/Server/coverage/lcov-report/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/Server/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/Server/coverage/lcov-report/prettify.js b/Server/coverage/lcov-report/prettify.js new file mode 100644 index 0000000..b322523 --- /dev/null +++ b/Server/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/Server/coverage/lcov-report/sort-arrow-sprite.png b/Server/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000..6ed6831 Binary files /dev/null and b/Server/coverage/lcov-report/sort-arrow-sprite.png differ diff --git a/Server/coverage/lcov-report/sorter.js b/Server/coverage/lcov-report/sorter.js new file mode 100644 index 0000000..2bb296a --- /dev/null +++ b/Server/coverage/lcov-report/sorter.js @@ -0,0 +1,196 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ( + row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()) + ) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/Server/coverage/lcov.info b/Server/coverage/lcov.info new file mode 100644 index 0000000..fdef420 --- /dev/null +++ b/Server/coverage/lcov.info @@ -0,0 +1,833 @@ +TN: +SF:app.js +FNF:0 +FNH:0 +DA:1,5 +DA:2,5 +DA:5,5 +DA:8,5 +DA:9,5 +DA:10,5 +DA:11,5 +DA:12,5 +DA:13,5 +DA:14,5 +DA:15,5 +DA:17,5 +DA:19,5 +DA:20,5 +DA:31,5 +DA:34,5 +DA:35,5 +DA:37,5 +DA:38,5 +DA:39,5 +DA:40,5 +DA:46,5 +LF:22 +LH:22 +BRDA:2,0,0,5 +BRDA:2,0,1,0 +BRF:2 +BRH:1 +end_of_record +TN: +SF:config\config.js +FNF:0 +FNH:0 +DA:1,5 +DA:3,5 +LF:2 +LH:2 +BRDA:14,0,0,5 +BRDA:14,0,1,0 +BRF:2 +BRH:1 +end_of_record +TN: +SF:controllers\authController.js +FN:9,(anonymous_0) +FN:28,(anonymous_1) +FN:70,(anonymous_2) +FNF:3 +FNH:3 +FNDA:11,(anonymous_0) +FNDA:11,(anonymous_1) +FNDA:3,(anonymous_2) +DA:1,5 +DA:2,5 +DA:3,5 +DA:5,5 +DA:6,5 +DA:8,5 +DA:10,11 +DA:11,11 +DA:13,11 +DA:14,1 +DA:24,10 +DA:29,11 +DA:30,11 +DA:31,11 +DA:32,3 +DA:34,8 +DA:35,8 +DA:36,1 +DA:38,7 +DA:39,2 +DA:41,5 +DA:42,1 +DA:47,4 +DA:48,3 +DA:49,1 +DA:51,2 +DA:52,2 +DA:53,1 +DA:55,1 +DA:56,1 +DA:66,8 +DA:71,3 +DA:72,3 +DA:73,3 +DA:74,1 +DA:76,2 +DA:80,2 +DA:81,2 +DA:82,1 +DA:83,1 +DA:90,1 +DA:91,1 +DA:101,2 +LF:43 +LH:43 +BRDA:31,0,0,3 +BRDA:31,0,1,8 +BRDA:35,1,0,1 +BRDA:35,1,1,7 +BRDA:38,2,0,2 +BRDA:38,2,1,5 +BRDA:41,3,0,1 +BRDA:41,3,1,4 +BRDA:48,4,0,1 +BRDA:48,4,1,2 +BRDA:52,5,0,1 +BRDA:52,5,1,1 +BRDA:73,6,0,1 +BRDA:73,6,1,2 +BRDA:82,7,0,1 +BRDA:82,7,1,0 +BRF:16 +BRH:15 +end_of_record +TN: +SF:controllers\bookingController.js +FN:5,(anonymous_0) +FN:27,(anonymous_1) +FN:55,(anonymous_2) +FN:112,(anonymous_3) +FN:148,(anonymous_4) +FN:170,(anonymous_5) +FNF:6 +FNH:5 +FNDA:1,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:4,(anonymous_2) +FNDA:1,(anonymous_3) +FNDA:2,(anonymous_4) +FNDA:2,(anonymous_5) +DA:1,5 +DA:3,5 +DA:6,1 +DA:7,1 +DA:9,1 +DA:20,1 +DA:23,0 +DA:28,0 +DA:29,0 +DA:31,0 +DA:43,0 +DA:44,0 +DA:48,0 +DA:49,0 +DA:51,0 +DA:56,4 +DA:57,4 +DA:58,4 +DA:61,4 +DA:62,4 +DA:63,1 +DA:67,3 +DA:92,3 +DA:93,1 +DA:99,2 +DA:106,2 +DA:108,2 +DA:113,1 +DA:114,1 +DA:115,1 +DA:116,1 +DA:117,1 +DA:120,1 +DA:121,0 +DA:128,1 +DA:129,0 +DA:135,1 +DA:142,1 +DA:144,0 +DA:149,2 +DA:150,2 +DA:151,2 +DA:153,2 +DA:154,2 +DA:157,1 +DA:158,1 +DA:159,0 +DA:162,1 +DA:164,1 +DA:166,1 +DA:171,2 +DA:172,2 +DA:173,2 +DA:174,2 +DA:175,1 +DA:176,1 +DA:178,1 +LF:57 +LH:44 +BRDA:43,0,0,0 +BRDA:43,0,1,0 +BRDA:62,1,0,1 +BRDA:62,1,1,3 +BRDA:92,2,0,1 +BRDA:92,2,1,2 +BRDA:117,3,0,0 +BRDA:117,3,1,1 +BRDA:120,4,0,0 +BRDA:120,4,1,1 +BRDA:120,5,0,1 +BRDA:120,5,1,1 +BRDA:128,6,0,0 +BRDA:128,6,1,1 +BRDA:136,7,0,1 +BRDA:136,7,1,0 +BRDA:137,8,0,1 +BRDA:137,8,1,0 +BRDA:138,9,0,1 +BRDA:138,9,1,0 +BRDA:139,10,0,1 +BRDA:139,10,1,1 +BRDA:154,11,0,1 +BRDA:154,11,1,1 +BRDA:158,12,0,0 +BRDA:158,12,1,1 +BRDA:174,13,0,1 +BRDA:174,13,1,1 +BRF:28 +BRH:19 +end_of_record +TN: +SF:controllers\courtController.js +FN:4,(anonymous_0) +FN:17,(anonymous_1) +FN:30,(anonymous_2) +FN:48,(anonymous_3) +FN:71,(anonymous_4) +FNF:5 +FNH:5 +FNDA:2,(anonymous_0) +FNDA:2,(anonymous_1) +FNDA:2,(anonymous_2) +FNDA:2,(anonymous_3) +FNDA:2,(anonymous_4) +DA:1,5 +DA:3,5 +DA:5,2 +DA:6,2 +DA:8,2 +DA:11,2 +DA:13,0 +DA:18,2 +DA:19,2 +DA:20,2 +DA:21,2 +DA:22,1 +DA:24,1 +DA:26,1 +DA:31,2 +DA:33,2 +DA:34,2 +DA:42,1 +DA:44,1 +DA:49,2 +DA:50,2 +DA:52,2 +DA:53,2 +DA:54,2 +DA:55,1 +DA:57,1 +DA:65,1 +DA:67,1 +DA:72,2 +DA:73,2 +DA:74,2 +DA:75,2 +DA:76,1 +DA:78,1 +DA:79,1 +DA:81,1 +LF:36 +LH:35 +BRDA:6,0,0,1 +BRDA:21,1,0,1 +BRDA:21,1,1,1 +BRDA:54,2,0,1 +BRDA:54,2,1,1 +BRDA:75,3,0,1 +BRDA:75,3,1,1 +BRF:7 +BRH:7 +end_of_record +TN: +SF:controllers\paymentController.js +FN:9,(anonymous_0) +FN:108,(anonymous_1) +FNF:2 +FNH:2 +FNDA:1,(anonymous_0) +FNDA:1,(anonymous_1) +DA:1,5 +DA:2,5 +DA:3,5 +DA:4,5 +DA:5,5 +DA:6,5 +DA:8,5 +DA:10,1 +DA:11,1 +DA:12,1 +DA:14,1 +DA:15,0 +DA:20,1 +DA:21,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,0 +DA:32,1 +DA:33,1 +DA:34,1 +DA:35,0 +DA:37,1 +DA:38,0 +DA:43,1 +DA:50,1 +DA:51,0 +DA:54,1 +DA:55,1 +DA:56,0 +DA:58,1 +DA:59,1 +DA:76,1 +DA:78,1 +DA:79,1 +DA:81,1 +DA:82,0 +DA:88,0 +DA:89,0 +DA:91,1 +DA:94,1 +DA:100,1 +DA:102,0 +DA:103,0 +DA:104,0 +DA:110,1 +DA:111,1 +DA:114,1 +DA:119,1 +DA:120,0 +DA:122,1 +DA:123,1 +DA:124,1 +DA:126,1 +DA:127,0 +DA:130,1 +DA:131,1 +DA:132,1 +DA:133,1 +DA:141,1 +DA:143,1 +DA:145,1 +DA:149,1 +DA:151,0 +DA:157,0 +LF:65 +LH:49 +BRDA:14,0,0,0 +BRDA:14,0,1,1 +BRDA:29,1,0,0 +BRDA:29,1,1,1 +BRDA:34,2,0,0 +BRDA:34,2,1,1 +BRDA:37,3,0,0 +BRDA:37,3,1,1 +BRDA:50,4,0,0 +BRDA:50,4,1,1 +BRDA:55,5,0,0 +BRDA:55,5,1,1 +BRDA:81,6,0,0 +BRDA:81,6,1,1 +BRDA:88,7,0,0 +BRDA:88,7,1,0 +BRDA:119,8,0,0 +BRDA:119,8,1,1 +BRDA:126,9,0,0 +BRDA:126,9,1,1 +BRDA:143,10,0,1 +BRDA:143,10,1,0 +BRDA:143,11,0,1 +BRDA:143,11,1,1 +BRF:24 +BRH:12 +end_of_record +TN: +SF:controllers\publicController.js +FN:5,(anonymous_0) +FN:40,(anonymous_1) +FNF:2 +FNH:2 +FNDA:3,(anonymous_0) +FNDA:2,(anonymous_1) +DA:1,5 +DA:2,5 +DA:4,5 +DA:6,3 +DA:7,3 +DA:8,3 +DA:10,3 +DA:11,1 +DA:17,3 +DA:18,1 +DA:21,3 +DA:22,3 +DA:27,3 +DA:36,0 +DA:41,2 +DA:42,2 +DA:43,2 +DA:44,2 +DA:45,1 +DA:47,1 +DA:49,1 +LF:21 +LH:20 +BRDA:7,0,0,2 +BRDA:7,1,0,3 +BRDA:7,2,0,3 +BRDA:10,3,0,1 +BRDA:10,3,1,2 +BRDA:10,4,0,3 +BRDA:10,4,1,1 +BRDA:17,5,0,1 +BRDA:17,5,1,2 +BRDA:44,6,0,1 +BRDA:44,6,1,1 +BRF:11 +BRH:11 +end_of_record +TN: +SF:helpers\AskGemini.js +FN:6,askGemini +FNF:1 +FNH:1 +FNDA:2,askGemini +DA:1,6 +DA:2,6 +DA:4,6 +DA:7,2 +DA:9,2 +DA:11,2 +DA:12,2 +DA:13,2 +DA:16,6 +LF:9 +LH:9 +BRF:0 +BRH:0 +end_of_record +TN: +SF:helpers\bcrypt.js +FN:3,(anonymous_0) +FN:8,(anonymous_1) +FNF:2 +FNH:2 +FNDA:7,(anonymous_0) +FNDA:2,(anonymous_1) +DA:1,5 +DA:3,5 +DA:4,7 +DA:5,7 +DA:8,5 +DA:9,2 +DA:12,5 +LF:7 +LH:7 +BRF:0 +BRH:0 +end_of_record +TN: +SF:helpers\jwt.js +FN:4,signToken +FN:8,verifyToken +FNF:2 +FNH:2 +FNDA:6,signToken +FNDA:23,verifyToken +DA:1,5 +DA:2,5 +DA:5,6 +DA:9,23 +DA:12,5 +LF:5 +LH:5 +BRDA:2,0,0,5 +BRDA:2,0,1,0 +BRF:2 +BRH:1 +end_of_record +TN: +SF:middleware\authentication.js +FN:5,authentication +FNF:1 +FNH:1 +FNDA:25,authentication +DA:1,5 +DA:2,5 +DA:5,5 +DA:7,25 +DA:8,25 +DA:9,25 +DA:10,25 +DA:11,2 +DA:12,2 +DA:14,23 +DA:16,23 +DA:18,23 +DA:20,23 +DA:22,23 +DA:23,0 +DA:26,23 +DA:28,23 +DA:30,0 +LF:18 +LH:16 +BRDA:10,0,0,2 +BRDA:10,0,1,23 +BRDA:22,1,0,0 +BRDA:22,1,1,23 +BRF:4 +BRH:3 +end_of_record +TN: +SF:middleware\errorHandler.js +FN:1,errorHandler +FNF:1 +FNH:1 +FNDA:33,errorHandler +DA:1,6 +DA:2,33 +DA:3,9 +DA:4,24 +DA:5,10 +DA:6,14 +DA:7,2 +DA:8,12 +DA:9,0 +DA:10,12 +DA:11,1 +DA:12,11 +DA:13,7 +DA:15,4 +LF:14 +LH:13 +BRDA:2,0,0,9 +BRDA:2,0,1,24 +BRDA:4,1,0,10 +BRDA:4,1,1,14 +BRDA:6,2,0,2 +BRDA:6,2,1,12 +BRDA:8,3,0,0 +BRDA:8,3,1,12 +BRDA:10,4,0,1 +BRDA:10,4,1,11 +BRDA:12,5,0,7 +BRDA:12,5,1,4 +BRF:12 +BRH:11 +end_of_record +TN: +SF:middleware\isAdmin.js +FN:1,authorizeAdmin +FNF:1 +FNH:1 +FNDA:15,authorizeAdmin +DA:1,5 +DA:2,15 +DA:3,1 +DA:8,14 +LF:4 +LH:4 +BRDA:2,0,0,1 +BRDA:2,0,1,14 +BRF:2 +BRH:2 +end_of_record +TN: +SF:models\booking.js +FN:3,(anonymous_0) +FN:10,(anonymous_1) +FNF:2 +FNH:2 +FNDA:5,(anonymous_0) +FNDA:5,(anonymous_1) +DA:2,5 +DA:3,5 +DA:12,5 +DA:13,5 +DA:14,5 +DA:17,5 +DA:44,5 +LF:7 +LH:7 +BRF:0 +BRH:0 +end_of_record +TN: +SF:models\court.js +FN:3,(anonymous_0) +FN:10,(anonymous_1) +FNF:2 +FNH:2 +FNDA:5,(anonymous_0) +FNDA:5,(anonymous_1) +DA:2,5 +DA:3,5 +DA:12,5 +DA:15,5 +DA:95,5 +LF:5 +LH:5 +BRF:0 +BRH:0 +end_of_record +TN: +SF:models\index.js +FN:25,(anonymous_0) +FN:33,(anonymous_1) +FN:41,(anonymous_2) +FNF:3 +FNH:3 +FNDA:25,(anonymous_0) +FNDA:20,(anonymous_1) +FNDA:20,(anonymous_2) +DA:3,5 +DA:4,5 +DA:5,5 +DA:6,5 +DA:7,5 +DA:8,5 +DA:9,5 +DA:10,5 +DA:13,5 +DA:14,0 +DA:16,5 +DA:24,5 +DA:26,25 +DA:34,20 +DA:38,20 +DA:41,5 +DA:42,20 +DA:43,20 +DA:47,5 +DA:48,5 +DA:50,5 +LF:21 +LH:20 +BRDA:8,0,0,5 +BRDA:8,0,1,0 +BRDA:13,1,0,0 +BRDA:13,1,1,5 +BRDA:27,2,0,25 +BRDA:27,2,1,25 +BRDA:27,2,2,20 +BRDA:27,2,3,20 +BRDA:42,3,0,20 +BRDA:42,3,1,0 +BRF:10 +BRH:7 +end_of_record +TN: +SF:models\payment.js +FN:3,(anonymous_0) +FN:5,(anonymous_1) +FNF:2 +FNH:2 +FNDA:5,(anonymous_0) +FNDA:5,(anonymous_1) +DA:2,5 +DA:3,5 +DA:7,5 +DA:10,5 +DA:71,5 +LF:5 +LH:5 +BRF:0 +BRH:0 +end_of_record +TN: +SF:models\user.js +FN:4,(anonymous_0) +FN:11,(anonymous_1) +FN:70,(anonymous_2) +FNF:3 +FNH:3 +FNDA:5,(anonymous_0) +FNDA:5,(anonymous_1) +FNDA:7,(anonymous_2) +DA:2,5 +DA:3,5 +DA:4,5 +DA:13,5 +DA:16,5 +DA:70,5 +DA:71,7 +DA:73,5 +LF:8 +LH:8 +BRF:0 +BRH:0 +end_of_record +TN: +SF:routes\ai.js +FN:7,(anonymous_0) +FNF:1 +FNH:1 +FNDA:2,(anonymous_0) +DA:1,6 +DA:2,6 +DA:3,6 +DA:5,6 +DA:7,6 +DA:8,2 +DA:9,2 +DA:10,2 +DA:14,2 +DA:15,1 +DA:17,1 +DA:21,6 +DA:23,6 +LF:13 +LH:13 +BRF:0 +BRH:0 +end_of_record +TN: +SF:routes\auth.js +FNF:0 +FNH:0 +DA:1,5 +DA:2,5 +DA:3,5 +DA:4,5 +DA:6,5 +DA:7,5 +DA:8,5 +DA:11,5 +DA:13,5 +LF:9 +LH:9 +BRF:0 +BRH:0 +end_of_record +TN: +SF:routes\bookings.js +FNF:0 +FNH:0 +DA:1,5 +DA:2,5 +DA:3,5 +DA:4,5 +DA:5,5 +DA:6,5 +DA:8,5 +DA:10,5 +DA:11,5 +DA:12,5 +DA:15,5 +DA:16,5 +DA:17,5 +DA:18,5 +DA:21,5 +DA:23,5 +LF:16 +LH:16 +BRF:0 +BRH:0 +end_of_record +TN: +SF:routes\courts.js +FNF:0 +FNH:0 +DA:1,5 +DA:2,5 +DA:3,5 +DA:4,5 +DA:5,5 +DA:6,5 +DA:9,5 +DA:12,5 +DA:15,5 +DA:18,5 +DA:21,5 +DA:24,5 +DA:27,5 +DA:29,5 +LF:14 +LH:14 +BRF:0 +BRH:0 +end_of_record +TN: +SF:routes\payments.js +FNF:0 +FNH:0 +DA:1,5 +DA:2,5 +DA:3,5 +DA:4,5 +DA:5,5 +DA:7,5 +DA:9,5 +DA:10,5 +DA:12,5 +DA:14,5 +LF:10 +LH:10 +BRF:0 +BRH:0 +end_of_record +TN: +SF:routes\public.js +FNF:0 +FNH:0 +DA:1,5 +DA:2,5 +DA:3,5 +DA:4,5 +DA:8,5 +DA:10,5 +DA:13,5 +DA:15,5 +LF:8 +LH:8 +BRF:0 +BRH:0 +end_of_record diff --git a/Server/helpers/AskGemini.js b/Server/helpers/AskGemini.js new file mode 100644 index 0000000..7ec7076 --- /dev/null +++ b/Server/helpers/AskGemini.js @@ -0,0 +1,16 @@ +const { GoogleGenerativeAI } = require("@google/generative-ai"); +require("dotenv").config(); + +const genAI = new GoogleGenerativeAI(process.env.GOOGLE_GENAI_API_KEY); + +async function askGemini(prompt) { + console.log("🔥 askGemini DIPANGGIL"); + + const model = genAI.getGenerativeModel({ model: "models/gemini-1.5-flash" }); + + const result = await model.generateContent(prompt); + const response = await result.response; + return response.text(); +} + +module.exports = { askGemini }; diff --git a/Server/helpers/bcrypt.js b/Server/helpers/bcrypt.js new file mode 100644 index 0000000..bd97a85 --- /dev/null +++ b/Server/helpers/bcrypt.js @@ -0,0 +1,15 @@ +const bcrypt = require("bcryptjs"); + +const hashPassword = (password) => { + const salt = bcrypt.genSaltSync(10); + return bcrypt.hashSync(password, salt); +}; + +const comparePassword = (plainPassword, hashedPassword) => { + return bcrypt.compareSync(plainPassword, hashedPassword); +}; + +module.exports = { + hashPassword, + comparePassword, +}; diff --git a/Server/helpers/jwt.js b/Server/helpers/jwt.js new file mode 100644 index 0000000..242e45a --- /dev/null +++ b/Server/helpers/jwt.js @@ -0,0 +1,12 @@ +const jwt = require("jsonwebtoken"); +const SECRET = process.env.JWT_SECRET || "sportify123"; + +function signToken(payload) { + return jwt.sign(payload, SECRET); +} + +function verifyToken(token) { + return jwt.verify(token, SECRET); +} + +module.exports = { signToken, verifyToken }; diff --git a/Server/middleware/authentication.js b/Server/middleware/authentication.js new file mode 100644 index 0000000..bbf57a6 --- /dev/null +++ b/Server/middleware/authentication.js @@ -0,0 +1,32 @@ +const { User } = require("../models"); +const { verifyToken } = require("../helpers/jwt"); + +//! authentication = untuk identify user/verifikasi(dia itu siapa?) +module.exports = async function authentication(req, res, next) { + //! Extract token: Get Bearer token from authorization header + console.log("Headers:", req.headers); + const bearerToken = req.headers.authorization; + console.log("🔐 Authorization Header:", bearerToken); + if (!bearerToken) { + next({ name: "Unauthorized", message: "Invalid token" }); //401 + return; + } + const access_token = bearerToken.split(" ")[1]; + + try { + //! Verify token + const data = verifyToken(access_token); + + const user = await User.findByPk(data.id); + + if (!user) { + throw { name: "Unauthorized", message: "Invalid token" }; //401 + } + //! Attach user + req.user = user; + //! continue: call next() to proceed + next(); + } catch (err) { + next(err); + } +}; diff --git a/Server/middleware/errorHandler.js b/Server/middleware/errorHandler.js new file mode 100644 index 0000000..362ba31 --- /dev/null +++ b/Server/middleware/errorHandler.js @@ -0,0 +1,17 @@ +module.exports = function errorHandler(err, req, res, next) { + if (err.name === "BadRequest") { + res.status(400).json({ message: err.message }); + } else if (err.name === "SequelizeValidationError") { + res.status(400).json({ message: err.errors[0].message }); + } else if (err.name === "Unauthorized") { + res.status(401).json({ message: err.message }); + } else if (err.name === "JsonWebTokenError") { + res.status(401).json({ message: "Invalid token" }); + } else if (err.name === "Forbidden") { + res.status(403).json({ message: err.message }); + } else if (err.name === "NotFound") { + res.status(404).json({ message: err.message }); + } else { + res.status(500).json({ message: "Internal Server Error" }); + } +}; diff --git a/Server/middleware/isAdmin.js b/Server/middleware/isAdmin.js new file mode 100644 index 0000000..d006234 --- /dev/null +++ b/Server/middleware/isAdmin.js @@ -0,0 +1,9 @@ +module.exports = function authorizeAdmin(req, res, next) { + if (req.user.role !== "admin") { + return next({ + name: "Forbidden", + message: "Only admin can access this resource", + }); // 403 + } + next(); +}; diff --git a/Server/migrations/20250722053500-create-user.js b/Server/migrations/20250722053500-create-user.js new file mode 100644 index 0000000..69b296a --- /dev/null +++ b/Server/migrations/20250722053500-create-user.js @@ -0,0 +1,40 @@ +"use strict"; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable("Users", { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + name: { + type: Sequelize.STRING, + }, + email: { + type: Sequelize.STRING, + unique: true, + allowNull: false, + }, + password: { + type: Sequelize.STRING, + }, + role: { + type: Sequelize.STRING, + defaultValue: "user", + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable("Users"); + }, +}; diff --git a/Server/migrations/20250722061706-create-court.js b/Server/migrations/20250722061706-create-court.js new file mode 100644 index 0000000..85a5108 --- /dev/null +++ b/Server/migrations/20250722061706-create-court.js @@ -0,0 +1,43 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('Courts', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + name: { + type: Sequelize.STRING + }, + category: { + type: Sequelize.STRING + }, + location: { + type: Sequelize.STRING + }, + pricePerHour: { + type: Sequelize.INTEGER + }, + description: { + type: Sequelize.TEXT + }, + imageUrl: { + type: Sequelize.STRING + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('Courts'); + } +}; \ No newline at end of file diff --git a/Server/migrations/20250722062000-create-booking.js b/Server/migrations/20250722062000-create-booking.js new file mode 100644 index 0000000..ad10eef --- /dev/null +++ b/Server/migrations/20250722062000-create-booking.js @@ -0,0 +1,59 @@ +"use strict"; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable("Bookings", { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + UserId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: "Users", + key: "id", + }, + onDelete: "CASCADE", + onUpdate: "CASCADE", + }, + CourtId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: "Courts", + key: "id", + }, + onDelete: "CASCADE", + onUpdate: "CASCADE", + }, + date: { + type: Sequelize.DATEONLY, + }, + timeStart: { + type: Sequelize.TIME, + }, + timeEnd: { + type: Sequelize.TIME, + }, + status: { + type: Sequelize.STRING, + allowNull: false, + defaultValue: "pending", + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable("Bookings"); + }, +}; diff --git a/Server/migrations/20250722062336-create-payment.js b/Server/migrations/20250722062336-create-payment.js new file mode 100644 index 0000000..614dbeb --- /dev/null +++ b/Server/migrations/20250722062336-create-payment.js @@ -0,0 +1,54 @@ +"use strict"; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable("Payments", { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + BookingId: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: "Bookings", + key: "id", + }, + onDelete: "CASCADE", + onUpdate: "CASCADE", + }, + amount: { + type: Sequelize.INTEGER, + allowNull: false, + }, + method: { + type: Sequelize.STRING, + defaultValue: "midtrans", + }, + status: { + type: Sequelize.STRING, + allowNull: false, + defaultValue: "pending", // or 'pending' + }, + paymentUrl: { + type: Sequelize.STRING, + }, + paidAt: { + type: Sequelize.DATE, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE, + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE, + }, + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable("Payments"); + }, +}; diff --git a/Server/migrations/20250724053255-add-isPaid-to-bookings.js b/Server/migrations/20250724053255-add-isPaid-to-bookings.js new file mode 100644 index 0000000..bd78fa6 --- /dev/null +++ b/Server/migrations/20250724053255-add-isPaid-to-bookings.js @@ -0,0 +1,15 @@ +"use strict"; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn("Bookings", "isPaid", { + type: Sequelize.BOOLEAN, + defaultValue: false, + }); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.removeColumn("Bookings", "isPaid"); + }, +}; diff --git a/Server/migrations/20250724184452-add-orderid-to-payments.js b/Server/migrations/20250724184452-add-orderid-to-payments.js new file mode 100644 index 0000000..ec3195b --- /dev/null +++ b/Server/migrations/20250724184452-add-orderid-to-payments.js @@ -0,0 +1,16 @@ +"use strict"; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.addColumn("Payments", "orderId", { + type: Sequelize.STRING, + allowNull: false, + unique: true, + }); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.removeColumn("Payments", "orderId"); + }, +}; diff --git a/Server/models/booking.js b/Server/models/booking.js new file mode 100644 index 0000000..df5429a --- /dev/null +++ b/Server/models/booking.js @@ -0,0 +1,45 @@ +"use strict"; +const { Model } = require("sequelize"); +module.exports = (sequelize, DataTypes) => { + class Booking extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + Booking.belongsTo(models.User, { foreignKey: "UserId" }); + Booking.belongsTo(models.Court, { foreignKey: "CourtId" }); + Booking.hasOne(models.Payment, { foreignKey: "BookingId" }); + } + } + Booking.init( + { + UserId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + CourtId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + date: DataTypes.DATEONLY, + timeStart: DataTypes.TIME, + timeEnd: DataTypes.TIME, + status: { + type: DataTypes.STRING, + defaultValue: "pending", + }, + isPaid: { + type: DataTypes.BOOLEAN, + defaultValue: false, + }, + }, + { + sequelize, + modelName: "Booking", + } + ); + return Booking; +}; diff --git a/Server/models/court.js b/Server/models/court.js new file mode 100644 index 0000000..745d867 --- /dev/null +++ b/Server/models/court.js @@ -0,0 +1,96 @@ +"use strict"; +const { Model } = require("sequelize"); +module.exports = (sequelize, DataTypes) => { + class Court extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + Court.hasMany(models.Booking, { foreignKey: "CourtId" }); + } + } + Court.init( + { + name: { + type: DataTypes.STRING, + allowNull: false, + validate: { + notNull: { + msg: "Name is required", + }, + notEmpty: { + msg: "Name is required", + }, + }, + }, + category: { + type: DataTypes.STRING, + allowNull: false, + validate: { + notNull: { + msg: "Category is required", + }, + notEmpty: { + msg: "Category is required", + }, + }, + }, + location: { + type: DataTypes.STRING, + allowNull: false, + validate: { + notNull: { + msg: "Location is required", + }, + notEmpty: { + msg: "Location is required", + }, + }, + }, + pricePerHour: { + type: DataTypes.INTEGER, + allowNull: false, + validate: { + notNull: { + msg: "Price is required", + }, + notEmpty: { + msg: "Price is required", + }, + }, + }, + description: { + type: DataTypes.TEXT, + allowNull: false, + validate: { + notNull: { + msg: "Description is required", + }, + notEmpty: { + msg: "Description is required", + }, + }, + }, + imageUrl: { + type: DataTypes.STRING, + allowNull: false, + validate: { + notNull: { + msg: "Image URL is required", + }, + notEmpty: { + msg: "Image URL is required", + }, + }, + }, + }, + { + sequelize, + modelName: "Court", + } + ); + return Court; +}; diff --git a/Server/models/index.js b/Server/models/index.js new file mode 100644 index 0000000..b4c77d5 --- /dev/null +++ b/Server/models/index.js @@ -0,0 +1,50 @@ +"use strict"; + +const fs = require("fs"); +const path = require("path"); +const Sequelize = require("sequelize"); +const process = require("process"); +const basename = path.basename(__filename); +const env = process.env.NODE_ENV || "development"; +const config = require(__dirname + "/../config/config.js")[env]; +const db = {}; + +let sequelize; +if (config.use_env_variable) { + sequelize = new Sequelize(process.env[config.use_env_variable], config); +} else { + sequelize = new Sequelize( + config.database, + config.username, + config.password, + config + ); +} + +fs.readdirSync(__dirname) + .filter((file) => { + return ( + file.indexOf(".") !== 0 && + file !== basename && + file.slice(-3) === ".js" && + file.indexOf(".test.js") === -1 + ); + }) + .forEach((file) => { + const model = require(path.join(__dirname, file))( + sequelize, + Sequelize.DataTypes + ); + db[model.name] = model; + }); + +Object.keys(db).forEach((modelName) => { + if (db[modelName].associate) { + db[modelName].associate(db); + } +}); + +db.sequelize = sequelize; +db.Sequelize = Sequelize; + +module.exports = db; diff --git a/Server/models/payment.js b/Server/models/payment.js new file mode 100644 index 0000000..512b6d8 --- /dev/null +++ b/Server/models/payment.js @@ -0,0 +1,72 @@ +"use strict"; +const { Model } = require("sequelize"); +module.exports = (sequelize, DataTypes) => { + class Payment extends Model { + static associate(models) { + // define association here + Payment.belongsTo(models.Booking, { foreignKey: "BookingId" }); + } + } + Payment.init( + { + BookingId: { + type: DataTypes.INTEGER, + allowNull: false, + validate: { + notNull: { msg: "BookingId is required" }, + isInt: { msg: "BookingId must be an integer" }, + }, + }, + amount: { + type: DataTypes.INTEGER, + allowNull: false, + validate: { + notNull: { msg: "Amount is required" }, + min: { args: [1], msg: "Amount must be at least 1" }, + isInt: { msg: "Amount must be a number" }, + }, + }, + method: { + type: DataTypes.STRING, + defaultValue: "midtrans", + }, + status: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: "pending", + validate: { + isIn: { + args: [["unpaid", "pending", "paid", "failed"]], + msg: "Status must be one of: unpaid, pending, paid, failed", + }, + }, + }, + paymentUrl: { + type: DataTypes.STRING, + validate: { + isUrl: { msg: "paymentUrl must be a valid URL" }, + }, + }, + paidAt: { + type: DataTypes.DATE, + }, + orderId: { + type: DataTypes.STRING, + allowNull: false, + validate: { + notNull: { + msg: "OrderId is required", + }, + notEmpty: { + msg: "OrderId cannot be empty", + }, + }, + }, + }, + { + sequelize, + modelName: "Payment", + } + ); + return Payment; +}; diff --git a/Server/models/user.js b/Server/models/user.js new file mode 100644 index 0000000..11475f7 --- /dev/null +++ b/Server/models/user.js @@ -0,0 +1,74 @@ +"use strict"; +const { Model } = require("sequelize"); +const { hashPassword } = require("../helpers/bcrypt"); +module.exports = (sequelize, DataTypes) => { + class User extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + User.hasMany(models.Booking, { foreignKey: "UserId" }); + } + } + User.init( + { + name: { + type: DataTypes.STRING, + allowNull: false, + validate: { + notEmpty: { + msg: "Name cannot be empty", + }, + notNull: { + msg: "Name is required", + }, + }, + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + validate: { + isEmail: true, + notEmpty: { + msg: "Email cannot be empty", + }, + notNull: { + msg: "Email is required", + }, + }, + }, + password: { + type: DataTypes.STRING, + allowNull: false, + validate: { + notEmpty: { + msg: "Password cannot be empty", + }, + notNull: { + msg: "Password is required", + }, + len: { + args: [6], + msg: "Password must be at least 6 characters long", + }, + }, + }, + role: { + type: DataTypes.STRING, + defaultValue: "user", + }, + }, + { + sequelize, + modelName: "User", + } + ); + User.beforeCreate((user) => { + user.password = hashPassword(user.password); + }); + return User; +}; diff --git a/Server/package-lock.json b/Server/package-lock.json new file mode 100644 index 0000000..a82add1 --- /dev/null +++ b/Server/package-lock.json @@ -0,0 +1,6872 @@ +{ + "name": "server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "server", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@google/generative-ai": "^0.7.0", + "axios": "^1.11.0", + "bcryptjs": "^3.0.2", + "cors": "^2.8.5", + "dayjs": "^1.11.13", + "dotenv": "^17.2.0", + "express": "^5.1.0", + "google-auth-library": "^10.1.0", + "jsonwebtoken": "^9.0.2", + "lucide-react": "^0.525.0", + "midtrans-client": "^1.4.3", + "pg": "^8.16.3", + "sequelize": "^6.37.7" + }, + "devDependencies": { + "jest": "^30.0.5", + "nodemon": "^3.1.10", + "sequelize-cli": "^6.6.3", + "supertest": "^7.1.3" + } + }, + "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/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "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.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", + "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/core": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", + "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", + "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@google/generative-ai": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.7.0.tgz", + "integrity": "sha512-+1kNkKbkDJftrvwfULQphkyC2ZQ42exlyR+TFARMuykuu6y8XyevbX0dDarqt1GVpZDm55yg0TBq6I1J65X6OA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.0.5.tgz", + "integrity": "sha512-xY6b0XiL0Nav3ReresUarwl2oIz1gTnxGbGpho9/rbUWsLH0f1OD/VT84xs8c7VmH7MChnLb0pag6PhZhAdDiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.0.5", + "jest-util": "30.0.5", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.0.5.tgz", + "integrity": "sha512-fKD0OulvRsXF1hmaFgHhVJzczWzA1RXMMo9LTPuFXo9q/alDbME3JIyWYqovWsUBWSoBcsHaGPSLF9rz4l9Qeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.0.5", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.0.5", + "@jest/test-result": "30.0.5", + "@jest/transform": "30.0.5", + "@jest/types": "30.0.5", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.0.5", + "jest-config": "30.0.5", + "jest-haste-map": "30.0.5", + "jest-message-util": "30.0.5", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.0.5", + "jest-resolve-dependencies": "30.0.5", + "jest-runner": "30.0.5", + "jest-runtime": "30.0.5", + "jest-snapshot": "30.0.5", + "jest-util": "30.0.5", + "jest-validate": "30.0.5", + "jest-watcher": "30.0.5", + "micromatch": "^4.0.8", + "pretty-format": "30.0.5", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.5.tgz", + "integrity": "sha512-aRX7WoaWx1oaOkDQvCWImVQ8XNtdv5sEWgk4gxR6NXb7WBUnL5sRak4WRzIQRZ1VTWPvV4VI4mgGjNL9TeKMYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.0.5", + "@jest/types": "30.0.5", + "@types/node": "*", + "jest-mock": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.5.tgz", + "integrity": "sha512-6udac8KKrtTtC+AXZ2iUN/R7dp7Ydry+Fo6FPFnDG54wjVMnb6vW/XNlf7Xj8UDjAE3aAVAsR4KFyKk3TCXmTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.0.5", + "jest-snapshot": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.5.tgz", + "integrity": "sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.5.tgz", + "integrity": "sha512-ZO5DHfNV+kgEAeP3gK3XlpJLL4U3Sz6ebl/n68Uwt64qFFs5bv4bfEEjyRGK5uM0C90ewooNgFuKMdkbEoMEXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.5", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.0.5", + "jest-mock": "30.0.5", + "jest-util": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", + "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.5.tgz", + "integrity": "sha512-7oEJT19WW4oe6HR7oLRvHxwlJk2gev0U9px3ufs8sX9PoD1Eza68KF0/tlN7X0dq/WVsBScXQGgCldA1V9Y/jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.0.5", + "@jest/expect": "30.0.5", + "@jest/types": "30.0.5", + "jest-mock": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.0.5.tgz", + "integrity": "sha512-mafft7VBX4jzED1FwGC1o/9QUM2xebzavImZMeqnsklgcyxBto8mV4HzNSzUrryJ+8R9MFOM3HgYuDradWR+4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.0.5", + "@jest/test-result": "30.0.5", + "@jest/transform": "30.0.5", + "@jest/types": "30.0.5", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.0.5", + "jest-util": "30.0.5", + "jest-worker": "30.0.5", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.5.tgz", + "integrity": "sha512-XcCQ5qWHLvi29UUrowgDFvV4t7ETxX91CbDczMnoqXPOIcZOxyNdSjm6kV5XMc8+HkxfRegU/MUmnTbJRzGrUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.5", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.0.5.tgz", + "integrity": "sha512-wPyztnK0gbDMQAJZ43tdMro+qblDHH1Ru/ylzUo21TBKqt88ZqnKKK2m30LKmLLoKtR2lxdpCC/P3g1vfKcawQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.0.5", + "@jest/types": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.0.5.tgz", + "integrity": "sha512-Aea/G1egWoIIozmDD7PBXUOxkekXl7ueGzrsGGi1SbeKgQqCYCIf+wfbflEbf2LiPxL8j2JZGLyrzZagjvW4YQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.0.5", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.5", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.5.tgz", + "integrity": "sha512-Vk8amLQCmuZyy6GbBht1Jfo9RSdBtg7Lks+B0PecnjI8J+PCLQPGh7uI8Q/2wwpW2gLdiAfiHNsmekKlywULqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.0.5", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.0", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.5", + "jest-regex-util": "30.0.1", + "jest-util": "30.0.5", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.5.tgz", + "integrity": "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.38", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz", + "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", + "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "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/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", + "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.15.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.2.tgz", + "integrity": "sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "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/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-jest": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.5.tgz", + "integrity": "sha512-mRijnKimhGDMsizTvBTWotwNpzrkHr+VvZUQBof2AufXKB8NXrL1W69TG20EvOz7aevx6FTJIaBuBkYxS8zolg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.0.5", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.0", + "babel-preset-jest": "30.0.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", + "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.1.tgz", + "integrity": "sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.1.tgz", + "integrity": "sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.0.1", + "babel-preset-current-node-syntax": "^1.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcryptjs": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", + "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "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.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", + "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "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": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/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": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/dotenv": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", + "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==", + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/editorconfig/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.190", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.190.tgz", + "integrity": "sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-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/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/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.5.tgz", + "integrity": "sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.0.5", + "@jest/get-type": "30.0.1", + "jest-matcher-utils": "30.0.5", + "jest-message-util": "30.0.5", + "jest-mock": "30.0.5", + "jest-util": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "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/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/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/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/form-data/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/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.1.tgz", + "integrity": "sha512-Odju3uBUJyVCkW64nLD4wKLhbh93bh6vIg/ZIXkWiLPBrdgtc65+tls/qml+un3pr6JqYVFDZbbmLDQT68rTOQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", + "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/google-auth-library": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.1.0.tgz", + "integrity": "sha512-GspVjZj1RbyRWpQ9FbAXMKjFGzZwDKnUHi66JJ+tcjcu5/xYAP1pdlWotCuIkMwjfVsxxDvsGZXGLzRt72D0sQ==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^7.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.1.tgz", + "integrity": "sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "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/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/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/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ], + "license": "MIT" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.5.tgz", + "integrity": "sha512-y2mfcJywuTUkvLm2Lp1/pFX8kTgMO5yyQGq/Sk/n2mN7XWYp4JsCZ/QXW34M8YScgk8bPZlREH04f6blPnoHnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.0.5", + "@jest/types": "30.0.5", + "import-local": "^3.2.0", + "jest-cli": "30.0.5" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.0.5.tgz", + "integrity": "sha512-bGl2Ntdx0eAwXuGpdLdVYVr5YQHnSZlQ0y9HVDu565lCUAe9sj6JOtBbMmBBikGIegne9piDDIOeiLVoqTkz4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.0.5", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.0.5.tgz", + "integrity": "sha512-h/sjXEs4GS+NFFfqBDYT7y5Msfxh04EwWLhQi0F8kuWpe+J/7tICSlswU8qvBqumR3kFgHbfu7vU6qruWWBPug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.0.5", + "@jest/expect": "30.0.5", + "@jest/test-result": "30.0.5", + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.0.5", + "jest-matcher-utils": "30.0.5", + "jest-message-util": "30.0.5", + "jest-runtime": "30.0.5", + "jest-snapshot": "30.0.5", + "jest-util": "30.0.5", + "p-limit": "^3.1.0", + "pretty-format": "30.0.5", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.0.5.tgz", + "integrity": "sha512-Sa45PGMkBZzF94HMrlX4kUyPOwUpdZasaliKN3mifvDmkhLYqLLg8HQTzn6gq7vJGahFYMQjXgyJWfYImKZzOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.0.5", + "@jest/test-result": "30.0.5", + "@jest/types": "30.0.5", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.0.5", + "jest-util": "30.0.5", + "jest-validate": "30.0.5", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.5.tgz", + "integrity": "sha512-aIVh+JNOOpzUgzUnPn5FLtyVnqc3TQHVMupYtyeURSb//iLColiMIR8TxCIDKyx9ZgjKnXGucuW68hCxgbrwmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.0.1", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.0.5", + "@jest/types": "30.0.5", + "babel-jest": "30.0.5", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-circus": "30.0.5", + "jest-docblock": "30.0.1", + "jest-environment-node": "30.0.5", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.0.5", + "jest-runner": "30.0.5", + "jest-util": "30.0.5", + "jest-validate": "30.0.5", + "micromatch": "^4.0.8", + "parse-json": "^5.2.0", + "pretty-format": "30.0.5", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.5.tgz", + "integrity": "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.0.1", + "chalk": "^4.1.2", + "pretty-format": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.0.1.tgz", + "integrity": "sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.0.5.tgz", + "integrity": "sha512-dKjRsx1uZ96TVyejD3/aAWcNKy6ajMaN531CwWIsrazIqIoXI9TnnpPlkrEYku/8rkS3dh2rbH+kMOyiEIv0xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1", + "@jest/types": "30.0.5", + "chalk": "^4.1.2", + "jest-util": "30.0.5", + "pretty-format": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.5.tgz", + "integrity": "sha512-ppYizXdLMSvciGsRsMEnv/5EFpvOdXBaXRBzFUDPWrsfmog4kYrOGWXarLllz6AXan6ZAA/kYokgDWuos1IKDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.0.5", + "@jest/fake-timers": "30.0.5", + "@jest/types": "30.0.5", + "@types/node": "*", + "jest-mock": "30.0.5", + "jest-util": "30.0.5", + "jest-validate": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.5.tgz", + "integrity": "sha512-dkmlWNlsTSR0nH3nRfW5BKbqHefLZv0/6LCccG0xFCTWcJu8TuEwG+5Cm75iBfjVoockmO6J35o5gxtFSn5xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.5", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.0.5", + "jest-worker": "30.0.5", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.0.5.tgz", + "integrity": "sha512-3Uxr5uP8jmHMcsOtYMRB/zf1gXN3yUIc+iPorhNETG54gErFIiUhLvyY/OggYpSMOEYqsmRxmuU4ZOoX5jpRFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1", + "pretty-format": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.5.tgz", + "integrity": "sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1", + "chalk": "^4.1.2", + "jest-diff": "30.0.5", + "pretty-format": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.5.tgz", + "integrity": "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.0.5", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.0.5", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.5.tgz", + "integrity": "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.5", + "@types/node": "*", + "jest-util": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.0.5.tgz", + "integrity": "sha512-d+DjBQ1tIhdz91B79mywH5yYu76bZuE96sSbxj8MkjWVx5WNdt1deEFRONVL4UkKLSrAbMkdhb24XN691yDRHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.5", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.0.5", + "jest-validate": "30.0.5", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.5.tgz", + "integrity": "sha512-/xMvBR4MpwkrHW4ikZIWRttBBRZgWK4d6xt3xW1iRDSKt4tXzYkMkyPfBnSCgv96cpkrctfXs6gexeqMYqdEpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.0.5.tgz", + "integrity": "sha512-JcCOucZmgp+YuGgLAXHNy7ualBx4wYSgJVWrYMRBnb79j9PD0Jxh0EHvR5Cx/r0Ce+ZBC4hCdz2AzFFLl9hCiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.0.5", + "@jest/environment": "30.0.5", + "@jest/test-result": "30.0.5", + "@jest/transform": "30.0.5", + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.0.1", + "jest-environment-node": "30.0.5", + "jest-haste-map": "30.0.5", + "jest-leak-detector": "30.0.5", + "jest-message-util": "30.0.5", + "jest-resolve": "30.0.5", + "jest-runtime": "30.0.5", + "jest-util": "30.0.5", + "jest-watcher": "30.0.5", + "jest-worker": "30.0.5", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.0.5.tgz", + "integrity": "sha512-7oySNDkqpe4xpX5PPiJTe5vEa+Ak/NnNz2bGYZrA1ftG3RL3EFlHaUkA1Cjx+R8IhK0Vg43RML5mJedGTPNz3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.0.5", + "@jest/fake-timers": "30.0.5", + "@jest/globals": "30.0.5", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.0.5", + "@jest/transform": "30.0.5", + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.5", + "jest-message-util": "30.0.5", + "jest-mock": "30.0.5", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.0.5", + "jest-snapshot": "30.0.5", + "jest-util": "30.0.5", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.5.tgz", + "integrity": "sha512-T00dWU/Ek3LqTp4+DcW6PraVxjk28WY5Ua/s+3zUKSERZSNyxTqhDXCWKG5p2HAJ+crVQ3WJ2P9YVHpj1tkW+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.0.5", + "@jest/get-type": "30.0.1", + "@jest/snapshot-utils": "30.0.5", + "@jest/transform": "30.0.5", + "@jest/types": "30.0.5", + "babel-preset-current-node-syntax": "^1.1.0", + "chalk": "^4.1.2", + "expect": "30.0.5", + "graceful-fs": "^4.2.11", + "jest-diff": "30.0.5", + "jest-matcher-utils": "30.0.5", + "jest-message-util": "30.0.5", + "jest-util": "30.0.5", + "pretty-format": "30.0.5", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.5.tgz", + "integrity": "sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.0.5.tgz", + "integrity": "sha512-ouTm6VFHaS2boyl+k4u+Qip4TSH7Uld5tyD8psQ8abGgt2uYYB8VwVfAHWHjHc0NWmGGbwO5h0sCPOGHHevefw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1", + "@jest/types": "30.0.5", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.0.5.tgz", + "integrity": "sha512-z9slj/0vOwBDBjN3L4z4ZYaA+pG56d6p3kTUhFRYGvXbXMWhXmb/FIxREZCD06DYUwDKKnj2T80+Pb71CQ0KEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.0.5", + "@jest/types": "30.0.5", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.0.5", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.5.tgz", + "integrity": "sha512-ojRXsWzEP16NdUuBw/4H/zkZdHOa7MMYCk4E430l+8fELeLg/mqmMlRhjL7UNZvQrDmnovWZV4DxX03fZF48fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.0.5", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/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/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "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/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/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/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/lucide-react": { + "version": "0.525.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.525.0.tgz", + "integrity": "sha512-Tm1txJ2OkymCGkvwoHt33Y2JpN5xucVq1slHcgE6Lk0WjDfjgKWor5CdVER8U6DvcfMwh4M8XxmpTiyzfmfDYQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/midtrans-client": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/midtrans-client/-/midtrans-client-1.4.3.tgz", + "integrity": "sha512-2oNEYgD8+FeUIOE5rUnLmqacbh3GHWvq+xKmL3dS4ZgCAiD5uzQQrKOz7tJ+7OOi3TbHDD8POLI2D6/xzQMCxw==", + "license": "MIT", + "dependencies": { + "axios": "^1.9.0", + "lodash": "^4.17.21" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.2.tgz", + "integrity": "sha512-tWVJxJHmBWLy69PvO96TZMZDrzmw5KeiZBz3RHmiM2XZ9grBJ2WgMAFVVg25nqp3ZjTFUs2Ftw1JhscL3Teliw==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "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/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nodemon/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "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-locate/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-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/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-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-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/pg": { + "version": "8.16.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", + "license": "MIT", + "dependencies": { + "pg-connection-string": "^2.9.1", + "pg-pool": "^3.10.1", + "pg-protocol": "^1.10.3", + "pg-types": "2.2.0", + "pgpass": "1.0.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.2.7" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", + "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", + "license": "MIT", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "license": "ISC", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", + "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", + "license": "MIT", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", + "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", + "license": "MIT" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "license": "MIT", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "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/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "license": "MIT", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pretty-format": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", + "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/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/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "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/retry-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", + "license": "MIT" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "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/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/sequelize": { + "version": "6.37.7", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz", + "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-cli": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.3.tgz", + "integrity": "sha512-1YYPrcSRt/bpMDDSKM5ubY1mnJ2TEwIaGZcqITw4hLtGtE64nIqaBnLtMvH8VKHg6FbWpXTiFNc2mS/BtQCXZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "^9.1.0", + "js-beautify": "1.15.4", + "lodash": "^4.17.21", + "picocolors": "^1.1.1", + "resolve": "^1.22.1", + "umzug": "^2.3.0", + "yargs": "^16.2.0" + }, + "bin": { + "sequelize": "lib/sequelize", + "sequelize-cli": "lib/sequelize" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sequelize-cli/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sequelize-cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/sequelize-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/sequelize-cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sequelize-cli/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sequelize-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/sequelize-cli/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sequelize-cli/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "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/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "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-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superagent": { + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.3.tgz", + "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==", + "dev": true, + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.1", + "cookiejar": "^2.1.4", + "debug": "^4.3.7", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.4", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.2" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supertest": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.4.tgz", + "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "methods": "^1.1.2", + "superagent": "^10.2.3" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", + "license": "MIT" + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/umzug": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", + "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.7.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "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/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validator": { + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "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/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "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/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/Server/package.json b/Server/package.json new file mode 100644 index 0000000..8315a97 --- /dev/null +++ b/Server/package.json @@ -0,0 +1,35 @@ +{ + "name": "server", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "dev": "nodemon server.js", + "start": "node server.js", + "test": "jest --coverage --runInBand --forceExit --verbose --silent" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@google/generative-ai": "^0.7.0", + "axios": "^1.11.0", + "bcryptjs": "^3.0.2", + "cors": "^2.8.5", + "dayjs": "^1.11.13", + "dotenv": "^17.2.0", + "express": "^5.1.0", + "google-auth-library": "^10.1.0", + "jsonwebtoken": "^9.0.2", + "lucide-react": "^0.525.0", + "midtrans-client": "^1.4.3", + "pg": "^8.16.3", + "sequelize": "^6.37.7" + }, + "devDependencies": { + "jest": "^30.0.5", + "nodemon": "^3.1.10", + "sequelize-cli": "^6.6.3", + "supertest": "^7.1.3" + } +} diff --git a/Server/routes/ai.js b/Server/routes/ai.js new file mode 100644 index 0000000..c856fb9 --- /dev/null +++ b/Server/routes/ai.js @@ -0,0 +1,23 @@ +const express = require("express"); +const { askGemini } = require("../helpers/AskGemini"); +const errorHandler = require("../middleware/errorHandler"); + +const router = express.Router(); + +router.post("/recommend", async (req, res, next) => { + try { + const { availableSlots, preference } = req.body; + const prompt = `Berikan rekomendasi waktu terbaik untuk booking berdasarkan preferensi berikut: ${preference}. Pilihan slot tersedia: ${availableSlots.join( + ", " + )}`; + + const recommendation = await askGemini(prompt); + res.json({ recommendation }); + } catch (err) { + next(err); + } +}); + +router.use(errorHandler); + +module.exports = router; diff --git a/Server/routes/auth.js b/Server/routes/auth.js new file mode 100644 index 0000000..c197d08 --- /dev/null +++ b/Server/routes/auth.js @@ -0,0 +1,13 @@ +const express = require("express"); +const router = express.Router(); +const authController = require("../controllers/authController"); +const errorHandler = require("../middleware/errorHandler"); + +router.post("/login", authController.login); +router.post("/register", authController.register); +router.post("/login/google", authController.googleLogin); + +//* error handler +router.use(errorHandler); + +module.exports = router; diff --git a/Server/routes/bookings.js b/Server/routes/bookings.js new file mode 100644 index 0000000..33fcf91 --- /dev/null +++ b/Server/routes/bookings.js @@ -0,0 +1,23 @@ +const express = require("express"); +const router = require("express").Router(); +const bookingController = require("../controllers/bookingController"); +const authentication = require("../middleware/authentication"); +const isAdmin = require("../middleware/isAdmin"); +const errorHandler = require("../middleware/errorHandler"); + +router.use(authentication); +// USER +router.get("/mine", bookingController.getMyBookings); +router.post("/", bookingController.createBooking); +router.patch("/:id", bookingController.updateBooking); // Allow user to update their own booking + +// ADMIN +router.get("/", isAdmin, bookingController.getAllBookings); +router.put("/:id", isAdmin, bookingController.updateBooking); +router.delete("/:id", isAdmin, bookingController.deleteBooking); +router.patch("/:id/status", isAdmin, bookingController.updateBookingStatus); + +//* error handler +router.use(errorHandler); + +module.exports = router; diff --git a/Server/routes/courts.js b/Server/routes/courts.js new file mode 100644 index 0000000..1cd4566 --- /dev/null +++ b/Server/routes/courts.js @@ -0,0 +1,29 @@ +const express = require("express"); +const router = express.Router(); +const courtController = require("../controllers/courtController"); +const authentication = require("../middleware/authentication"); +const isAdmin = require("../middleware/isAdmin"); +const errorHandler = require("../middleware/errorHandler"); + +// Semua endpoint di sini harus login dulu +router.use(authentication); //* Semua route setelah ini butuh login + +// GET semua lapangan (semua user bisa lihat) +router.get("/", isAdmin, courtController.getAllCourts); + +// GET detail lapangan by id +router.get("/:id", isAdmin, courtController.getCourtById); + +// POST buat tambah lapangan (hanya admin) +router.post("/", isAdmin, courtController.createCourt); + +// PUT edit lapangan (admin) +router.put("/:id", isAdmin, courtController.updateCourt); + +// DELETE hapus lapangan (admin) +router.delete("/:id", isAdmin, courtController.deleteCourt); + +//* error handler +router.use(errorHandler); + +module.exports = router; diff --git a/Server/routes/payments.js b/Server/routes/payments.js new file mode 100644 index 0000000..4b4aa8b --- /dev/null +++ b/Server/routes/payments.js @@ -0,0 +1,14 @@ +const express = require("express"); +const router = express.Router(); +const authentication = require("../middleware/authentication"); +const paymentController = require("../controllers/paymentController"); +const errorHandler = require("../middleware/errorHandler"); + +router.use(authentication); + +router.patch("/me/upgrade", paymentController.upgradeAccount); // hanya user yang login +router.post("/midtrans/initiate", paymentController.initiateMidtransTrx); +// error handler +router.use(errorHandler); + +module.exports = router; diff --git a/Server/routes/public.js b/Server/routes/public.js new file mode 100644 index 0000000..ccdbb71 --- /dev/null +++ b/Server/routes/public.js @@ -0,0 +1,15 @@ +const express = require("express"); +const router = express.Router(); +const publicController = require("../controllers/publicController"); +const errorHandler = require("../middleware/errorHandler"); + +//* Public routes +// GET semua lapangan (semua user bisa lihat) +router.get("/courts", publicController.getCourts); +// GET detail lapangan by id +router.get("/courts/:id", publicController.getCourtsById); + +//* error handler +router.use(errorHandler); + +module.exports = router; diff --git a/Server/seeders/20250722064248-demo-users.js b/Server/seeders/20250722064248-demo-users.js new file mode 100644 index 0000000..c45ac0d --- /dev/null +++ b/Server/seeders/20250722064248-demo-users.js @@ -0,0 +1,33 @@ +"use strict"; + +const { hashPassword } = require("../helpers/bcrypt"); + +// const bcrypt = require("bcryptjs"); + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.bulkInsert("Users", [ + { + name: "Admin Sportify", + email: "admin@sportify.com", + password: hashPassword("12345678"), + role: "admin", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "User Sportify", + email: "user@sportify.com", + password: hashPassword("12345678"), + role: "user", + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.bulkDelete("Users", null, {}); + }, +}; diff --git a/Server/seeders/20250722164134-demo-courts.js b/Server/seeders/20250722164134-demo-courts.js new file mode 100644 index 0000000..90b2fce --- /dev/null +++ b/Server/seeders/20250722164134-demo-courts.js @@ -0,0 +1,235 @@ +"use strict"; + +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.bulkInsert("Courts", [ + { + name: "Lapangan Futsal Senayan", + category: "Futsal", + location: "Jakarta Selatan", + pricePerHour: 150000, + description: "Lapangan futsal indoor berstandar nasional.", + imageUrl: + "https://images.unsplash.com/photo-1716745558902-c4d7a19a397d?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MzV8fGZ1dHNhbHxlbnwwfHwwfHx8MA%3D%3D", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Mini Soccer Pondok Indah", + category: "Mini Soccer", + location: "Jakarta Selatan", + pricePerHour: 200000, + description: + "Lapangan mini soccer rumput sintetis, cocok untuk 7 lawan 7.", + imageUrl: + "https://images.unsplash.com/photo-1517747614396-d21a78b850e8?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Nnx8bWluaSUyMHNvY2NlcnxlbnwwfHwwfHx8MA%3D%3D", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "GOR Badminton Cempaka", + category: "Badminton", + location: "Bandung", + pricePerHour: 80000, + description: "Tersedia 4 lapangan indoor dengan lantai karpet.", + imageUrl: + "https://images.unsplash.com/photo-1626926938421-90124a4b83fa?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mzl8fGJhZG1pbnRvbnxlbnwwfHwwfHx8MA%3D%3D", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "GOR Volley Surabaya", + category: "Volley", + location: "Surabaya", + pricePerHour: 100000, + description: + "Lapangan volley indoor full net, cocok untuk turnamen lokal.", + imageUrl: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSBWtFeB-nqsRyFyeV8mLH8MScyK9K3d5G5MQ&s", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Stadion Tenis Rawamangun", + category: "Tennis", + location: "Jakarta Timur", + pricePerHour: 120000, + description: + "Lapangan tenis dengan permukaan keras dan penerangan malam.", + imageUrl: + "https://images.unsplash.com/photo-1568478624365-1134fbd7c895?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTA0fHx2b2xsZXl8ZW58MHx8MHx8fDA%3D", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Futsal Galaxy Arena", + category: "Futsal", + location: "Bekasi", + pricePerHour: 130000, + description: "Lapangan futsal dengan AC dan tribune penonton.", + imageUrl: + "https://images.unsplash.com/photo-1521217078329-f8fc1becab68?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8OHx8ZnV0c2FsfGVufDB8fDB8fHww", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Lapangan Basket Cibubur", + category: "Basketball", + location: "Jakarta Timur", + pricePerHour: 100000, + description: "Lapangan outdoor dengan lantai beton dan papan akrilik.", + imageUrl: + "https://images.unsplash.com/photo-1509027572446-af8401acfdc3?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MjB8fGJhc2tldHxlbnwwfHwwfHx8MA%3D%3D", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Lapangan Tennis Palembang", + category: "Tennis", + location: "Palembang", + pricePerHour: 110000, + description: "Tennis court clay dan hard court tersedia.", + imageUrl: + "https://images.unsplash.com/photo-1480180566821-a7d525cdfc5e?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTR8fHRlbm5pc3xlbnwwfHwwfHx8MA%3D%3D", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Lapangan Futsal Dago", + category: "Futsal", + location: "Bandung", + pricePerHour: 140000, + description: "Lapangan futsal indoor rumput sintetis.", + imageUrl: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRPXgPcFLGd2xqqrlYlXriV7JpHChaOFFaqZA&s", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Lapangan Basket BSD", + category: "Basketball", + location: "Tangerang Selatan", + pricePerHour: 125000, + description: "Lapangan indoor dengan AC dan papan skor elektronik.", + imageUrl: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRIqF5EXjj-0_LYDLKe4np6ng3aLUeRzTdjZw&s", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "GOR Badminton Malang", + category: "Badminton", + location: "Malang", + pricePerHour: 90000, + description: "Lapangan karpet internasional dengan pencahayaan LED.", + imageUrl: + "https://plus.unsplash.com/premium_photo-1663039984787-b11d7240f592?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MzN8fGJhZG1pbnRvbnxlbnwwfHwwfHx8MA%3D%3D", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Mini Soccer Taman Mini", + category: "Mini Soccer", + location: "Jakarta Timur", + pricePerHour: 180000, + description: "Mini soccer dengan ruang tunggu nyaman.", + imageUrl: + "https://images.unsplash.com/photo-1616778551732-6dd1289f567d?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTUzfHx2b2xsZXl8ZW58MHx8MHx8fDA%3D", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "GOR Volley Manado", + category: "Volley", + location: "Manado", + pricePerHour: 95000, + description: "Lapangan volley dengan tribun penonton.", + imageUrl: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTIgD47eHjui8wqalojPnP1opTWUZGMXF-9qg&s", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Tennis Court Gading Serpong", + category: "Tennis", + location: "Tangerang", + pricePerHour: 115000, + description: "Court outdoor dengan pencahayaan LED.", + imageUrl: + "https://images.unsplash.com/photo-1499510318569-1a3d67dc3976?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Nnx8dGVubmlzfGVufDB8fDB8fHww", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Lapangan Futsal Denpasar", + category: "Futsal", + location: "Denpasar", + pricePerHour: 135000, + description: "Futsal indoor dengan fasilitas shower dan parkir luas.", + imageUrl: + "https://images.pexels.com/photos/46798/the-ball-stadion-football-the-pitch-46798.jpeg", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "GOR Badminton Makassar", + category: "Badminton", + location: "Makassar", + pricePerHour: 95000, + description: "4 lapangan dengan AC dan kantin.", + imageUrl: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRLX8-zLvd68YE_5CtHYSDTeD7h-V__WSPLBA&s", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Lapangan Volley Yogyakarta", + category: "Volley", + location: "Yogyakarta", + pricePerHour: 90000, + description: "Volley court outdoor full pasir.", + imageUrl: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTrSvZy4uT_AlyGGzeoMGHpXRTZj87rCcbPIA&s", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Basket Court Tebet", + category: "Basketball", + location: "Jakarta Selatan", + pricePerHour: 110000, + description: "Lapangan semi indoor dengan papan skor digital.", + imageUrl: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTytxjKVoYZbYCrkmjq6Ofd1VLR-XDLg6bPmA&s", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Lapangan Tennis Bali", + category: "Tennis", + location: "Bali", + pricePerHour: 130000, + description: "Tennis court dengan pemandangan pantai.", + imageUrl: + "https://images.unsplash.com/photo-1620742820748-87c09249a72a?w=600&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxzZWFyY2h8N3x8dGVubmlzfGVufDB8fDB8fHww", + createdAt: new Date(), + updatedAt: new Date(), + }, + { + name: "Mini Soccer Ciputat", + category: "Mini Soccer", + location: "Tangerang Selatan", + pricePerHour: 190000, + description: "Mini soccer outdoor rumput sintetis dan cafetaria.", + imageUrl: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSjkN-zlIEkKExI00fk_0ZybZO1FoyErY1o9w&s", + createdAt: new Date(), + updatedAt: new Date(), + }, + ]); + }, + + async down(queryInterface, Sequelize) { + await queryInterface.bulkDelete("Courts", null, {}); + }, +}; diff --git a/Server/server.js b/Server/server.js new file mode 100644 index 0000000..7c7d245 --- /dev/null +++ b/Server/server.js @@ -0,0 +1,18 @@ +// server.js +require("dotenv").config(); +const cors = require("cors"); +const app = require("./app"); + +app.use( + cors({ + origin: "https://sportifycourts.web.app", // <- GANTI sesuai domain client kamu + // credentials: true, // kalau pakai cookie auth (optional) + }) +); +console.log("NODE_ENV:", process.env.NODE_ENV); + +const PORT = process.env.PORT || 3000; + +app.listen(PORT, () => { + console.log(`🚀 Server running at http://localhost:${PORT}`); +}); diff --git a/api-docs.md b/api-docs.md new file mode 100644 index 0000000..a335709 --- /dev/null +++ b/api-docs.md @@ -0,0 +1,383 @@ +## API Documentation + +### **Authentication** + +#### **1. Register User** + +- **Endpoint**: `POST /auth/register` +- **Description**: Mendaftarkan user baru. +- **Request Body**: + ```json + { + "name": "string", + "email": "string", + "password": "string" + } + ``` +- **Response**: + - **201 Created**: + ```json + { + "message": "User registered successfully", + "user": { + "id": "integer", + "name": "string", + "email": "string", + "role": "string" + } + } + ``` + - **400 Bad Request**: + ```json + { + "message": "Email is already registered" + } + ``` + +#### **2. Login User** + +- **Endpoint**: `POST /auth/login` +- **Description**: Login user dan generate access token. +- **Request Body**: + ```json + { + "email": "string", + "password": "string" + } + ``` +- **Response**: + - **200 OK**: + ```json + { + "message": "Login Success", + "access_token": "string", + "user": { + "id": "integer", + "name": "string", + "email": "string", + "role": "string" + } + } + ``` + - **401 Unauthorized**: + ```json + { + "message": "Invalid email or password" + } + ``` + +#### **3. Google Login** + +- **Endpoint**: `POST /auth/login/google` +- **Description**: Login menggunakan Google OAuth. +- **Request Body**: + ```json + { + "id_token": "string" + } + ``` +- **Response**: + - **200 OK**: + ```json + { + "message": "Google Login Success", + "access_token": "string", + "user": { + "id": "integer", + "name": "string", + "email": "string", + "role": "string" + } + } + ``` + - **401 Unauthorized**: + ```json + { + "message": "Invalid Google token" + } + ``` + +### **Users** + +#### **1. Get All Users** + +- **Endpoint**: `GET /users` +- **Description**: Mendapatkan semua data user (admin only). +- **Headers**: + ```json + { + "Authorization": "Bearer " + } + ``` +- **Response**: + - **200 OK**: + ```json + [ + { + "id": "integer", + "name": "string", + "email": "string", + "role": "string" + } + ] + ``` + +#### **2. Get User by ID** + +- **Endpoint**: `GET /users/:id` +- **Description**: Mendapatkan detail user berdasarkan ID. +- **Response**: + - **200 OK**: + ```json + { + "id": "integer", + "name": "string", + "email": "string", + "role": "string" + } + ``` + +#### **3. Delete User** + +- **Endpoint**: `DELETE /users/:id` +- **Description**: Menghapus user berdasarkan ID (admin only). +- **Response**: + - **200 OK**: + ```json + { + "message": "User deleted successfully" + } + ``` + +### **Public** + +#### **1. Get Public Courts** + +- **Endpoint**: `GET /public/courts` +- **Description**: Mendapatkan semua lapangan untuk publik (tanpa autentikasi). +- **Query Parameters**: + - `page`: Nomor halaman (default: 1) + - `limit`: Jumlah data per halaman (default: 10) + - `search`: Pencarian berdasarkan nama atau lokasi + - `category`: Filter berdasarkan kategori +- **Response**: + - **200 OK**: + ```json + { + "courts": [ + { + "id": "integer", + "name": "string", + "location": "string", + "pricePerHour": "integer", + "description": "string", + "imageUrl": "string", + "category": "string" + } + ], + "pagination": { + "page": "integer", + "limit": "integer", + "total": "integer", + "totalPages": "integer" + } + } + ``` + +#### **2. Get Public Court by ID** + +- **Endpoint**: `GET /public/courts/:id` +- **Description**: Mendapatkan detail lapangan untuk publik. +- **Response**: + - **200 OK**: + ```json + { + "id": "integer", + "name": "string", + "location": "string", + "pricePerHour": "integer", + "description": "string", + "imageUrl": "string", + "category": "string" + } + ``` + +### **Courts** + +#### **1. Get All Courts** + +- **Endpoint**: `GET /courts` +- **Description**: Mendapatkan semua lapangan (dapat difilter berdasarkan kategori atau lokasi). +- **Query Parameters**: + - `category`: Filter berdasarkan kategori. + - `location`: Filter berdasarkan lokasi. +- **Response**: + - **200 OK**: + ```json + [ + { + "id": "integer", + "name": "string", + "location": "string", + "pricePerHour": "integer", + "description": "string", + "imageUrl": "string", + "category": "string" + } + ] + ``` + +#### **2. Get Court by ID** + +- **Endpoint**: `GET /courts/:id` +- **Description**: Mendapatkan detail lapangan berdasarkan ID. +- **Response**: + - **200 OK**: + ```json + { + "id": "integer", + "name": "string", + "location": "string", + "pricePerHour": "integer", + "description": "string", + "imageUrl": "string", + "category": "string" + } + ``` + +#### **3. Create Court** + +- **Endpoint**: `POST /courts` +- **Description**: Menambahkan lapangan baru (admin only). +- **Request Body**: + ```json + { + "name": "string", + "location": "string", + "pricePerHour": "integer", + "description": "string", + "imageUrl": "string", + "category": "string" + } + ``` +- **Response**: + - **201 Created**: + ```json + { + "message": "Court created successfully" + } + ``` + +#### **4. Update Court** + +- **Endpoint**: `PUT /courts/:id` +- **Description**: Mengedit lapangan berdasarkan ID (admin only). +- **Request Body**: + ```json + { + "name": "string", + "location": "string", + "pricePerHour": "integer", + "description": "string", + "imageUrl": "string", + "category": "string" + } + ``` +- **Response**: + - **200 OK**: + ```json + { + "message": "Court updated successfully" + } + ``` + +#### **5. Delete Court** + +- **Endpoint**: `DELETE /courts/:id` +- **Description**: Menghapus lapangan berdasarkan ID (admin only). +- **Response**: + - **200 OK**: + ```json + { + "message": "Court deleted successfully" + } + ``` + +### **Bookings** + +#### **1. Get All Bookings** + +- **Endpoint**: `GET /bookings` +- **Description**: Mendapatkan semua booking milik user yang login. +- **Headers**: + ```json + { + "Authorization": "Bearer " + } + ``` +- **Response**: + - **200 OK**: + ```json + [ + { + "id": "integer", + "courtId": "integer", + "date": "string", + "timeStart": "string", + "timeEnd": "string", + "status": "string", + "paymentUrl": "string" + } + ] + ``` + +#### **2. Create Booking** + +- **Endpoint**: `POST /bookings` +- **Description**: Membuat booking baru. +- **Request Body**: + ```json + { + "courtId": "integer", + "date": "string", + "timeStart": "string", + "timeEnd": "string" + } + ``` +- **Response**: + - **201 Created**: + ```json + { + "message": "Booking created successfully", + "booking": { + "id": "integer", + "courtId": "integer", + "date": "string", + "timeStart": "string", + "timeEnd": "string", + "status": "string", + "paymentUrl": "string" + } + } + ``` + +### **Webhook** + +#### **1. Payment Webhook** + +- **Endpoint**: `POST /payment/webhook` +- **Description**: Callback dari Midtrans untuk memperbarui status pembayaran. +- **Request Body**: + ```json + { + "order_id": "string", + "transaction_status": "string" + } + ``` +- **Response**: + - **200 OK**: + ```json + { + "message": "Payment status updated" + } + ```