From b2b268f1e52c964b72603d736e9fde987007f926 Mon Sep 17 00:00:00 2001 From: HYOJIN Date: Tue, 7 Apr 2026 15:42:23 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8?= =?UTF-8?q?=20=EC=B4=88=EA=B8=B0=20=EC=84=A4=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=ED=8C=80=20=EC=9B=8C=ED=81=AC=ED=94=8C=EB=A1=9C=EC=9A=B0=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Spring Boot 3.2.1 + React 19 프로젝트 구조 - S&P Global Maritime API Bypass 및 Risk & Compliance Screening 기능 - 팀 워크플로우 v1.6.1 적용 (settings.json, hooks, workflow-version) - 프론트엔드 빌드 (Vite + TypeScript + Tailwind CSS) - 메인 카드 레이아웃 CSS Grid 전환 Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/settings.json | 87 + .claude/workflow-version.json | 6 + .editorconfig | 33 + .gitattributes | 1 + .gitea/workflows/deploy.yml | 49 + .githooks/commit-msg | 60 + .githooks/post-checkout | 25 + .githooks/pre-commit | 33 + .gitignore | 120 + .mvn/settings.xml | 22 + .sdkmanrc | 1 + CLAUDE.md | 104 + README.md | 106 +- frontend/.gitignore | 24 + frontend/README.md | 73 + frontend/eslint.config.js | 23 + frontend/index.html | 17 + frontend/package-lock.json | 3935 +++++++++++++++++ frontend/package.json | 33 + frontend/public/android-chrome-192x192.png | Bin 0 -> 12478 bytes frontend/public/android-chrome-512x512.png | Bin 0 -> 26995 bytes frontend/public/apple-touch-icon.png | Bin 0 -> 11070 bytes frontend/public/favicon-16x16.png | Bin 0 -> 707 bytes frontend/public/favicon-32x32.png | Bin 0 -> 1643 bytes frontend/public/favicon.ico | Bin 0 -> 15406 bytes frontend/public/site.webmanifest | 1 + frontend/src/App.tsx | 69 + frontend/src/api/bypassAccountApi.ts | 161 + frontend/src/api/bypassApi.ts | 109 + frontend/src/api/screeningGuideApi.ts | 149 + frontend/src/components/ConfirmModal.tsx | 49 + frontend/src/components/CopyButton.tsx | 48 + frontend/src/components/EmptyState.tsx | 15 + frontend/src/components/GuideModal.tsx | 92 + frontend/src/components/InfoItem.tsx | 15 + frontend/src/components/InfoModal.tsx | 35 + frontend/src/components/LoadingSpinner.tsx | 7 + frontend/src/components/Navbar.tsx | 137 + frontend/src/components/Pagination.tsx | 145 + frontend/src/components/StatusBadge.tsx | 40 + frontend/src/components/Toast.tsx | 37 + .../components/bypass/BypassConfigModal.tsx | 222 + .../src/components/bypass/BypassStepBasic.tsx | 142 + .../components/bypass/BypassStepParams.tsx | 157 + .../components/screening/ComplianceTab.tsx | 230 + .../src/components/screening/HistoryTab.tsx | 954 ++++ .../components/screening/MethodologyTab.tsx | 209 + frontend/src/components/screening/RiskTab.tsx | 187 + frontend/src/constants/screeningTexts.ts | 170 + frontend/src/contexts/ThemeContext.tsx | 26 + frontend/src/contexts/ToastContext.tsx | 29 + frontend/src/hooks/usePoller.ts | 53 + frontend/src/hooks/useTheme.ts | 27 + frontend/src/hooks/useToast.ts | 27 + frontend/src/index.css | 3 + frontend/src/main.tsx | 10 + frontend/src/pages/BypassAccessRequest.tsx | 563 +++ .../src/pages/BypassAccountManagement.tsx | 486 ++ frontend/src/pages/BypassAccountRequests.tsx | 700 +++ frontend/src/pages/BypassCatalog.tsx | 320 ++ frontend/src/pages/BypassConfig.tsx | 524 +++ frontend/src/pages/MainMenu.tsx | 60 + frontend/src/pages/RiskComplianceHistory.tsx | 44 + frontend/src/pages/ScreeningGuide.tsx | 87 + frontend/src/theme/base.css | 100 + frontend/src/theme/tokens.css | 84 + frontend/src/utils/formatters.ts | 58 + frontend/tsconfig.app.json | 28 + frontend/tsconfig.json | 7 + frontend/tsconfig.node.json | 26 + frontend/vite.config.ts | 21 + package-lock.json | 6 + pom.xml | 190 + .../com/snp/batch/SnpGlobalApplication.java | 14 + .../api/logging/ApiAccessLoggingFilter.java | 149 + .../com/snp/batch/common/web/ApiResponse.java | 75 + .../web/controller/BaseBypassController.java | 45 + .../common/web/service/BaseBypassService.java | 131 + .../config/BypassApiUserDetailsService.java | 51 + .../BypassAuthenticationEntryPoint.java | 56 + .../config/MaritimeApiWebClientConfig.java | 161 + .../batch/global/config/SecurityConfig.java | 45 + .../batch/global/config/SwaggerConfig.java | 154 + .../controller/BypassAccountController.java | 124 + .../controller/BypassConfigController.java | 131 + .../controller/ScreeningGuideController.java | 146 + .../global/controller/WebViewController.java | 23 + .../batch/global/dto/BypassConfigRequest.java | 39 + .../global/dto/BypassConfigResponse.java | 57 + .../snp/batch/global/dto/BypassParamDto.java | 39 + .../global/dto/CodeGenerationResult.java | 31 + .../dto/bypass/BypassAccountResponse.java | 32 + .../bypass/BypassAccountUpdateRequest.java | 22 + .../dto/bypass/BypassRequestResponse.java | 33 + .../bypass/BypassRequestReviewRequest.java | 19 + .../bypass/BypassRequestSubmitRequest.java | 22 + .../batch/global/dto/bypass/ServiceIpDto.java | 17 + .../dto/screening/ChangeHistoryResponse.java | 24 + .../dto/screening/CompanyInfoResponse.java | 26 + .../screening/ComplianceCategoryResponse.java | 20 + .../ComplianceIndicatorResponse.java | 22 + .../screening/IndicatorStatusResponse.java | 17 + .../screening/MethodologyHistoryResponse.java | 19 + .../dto/screening/RiskCategoryResponse.java | 19 + .../dto/screening/RiskIndicatorResponse.java | 22 + .../dto/screening/ShipInfoResponse.java | 23 + .../snp/batch/global/model/AccountStatus.java | 5 + .../batch/global/model/BypassApiAccount.java | 121 + .../batch/global/model/BypassApiConfig.java | 153 + .../batch/global/model/BypassApiParam.java | 83 + .../batch/global/model/BypassApiRequest.java | 147 + .../global/model/BypassApiServiceIp.java | 45 + .../snp/batch/global/model/RequestStatus.java | 5 + .../global/model/screening/ChangeType.java | 25 + .../model/screening/ChangeTypeLang.java | 31 + .../model/screening/ChangeTypeLangId.java | 33 + .../screening/CompanyComplianceHistory.java | 45 + .../model/screening/CompanyDetailInfo.java | 61 + .../model/screening/ComplianceCategory.java | 28 + .../screening/ComplianceCategoryLang.java | 31 + .../screening/ComplianceCategoryLangId.java | 33 + .../model/screening/ComplianceIndicator.java | 44 + .../screening/ComplianceIndicatorLang.java | 43 + .../screening/ComplianceIndicatorLangId.java | 33 + .../global/model/screening/Language.java | 28 + .../model/screening/MethodologyHistory.java | 36 + .../screening/MethodologyHistoryLang.java | 31 + .../screening/MethodologyHistoryLangId.java | 33 + .../global/model/screening/RiskIndicator.java | 40 + .../screening/RiskIndicatorCategory.java | 25 + .../screening/RiskIndicatorCategoryLang.java | 31 + .../RiskIndicatorCategoryLangId.java | 33 + .../model/screening/RiskIndicatorLang.java | 43 + .../model/screening/RiskIndicatorLangId.java | 33 + .../screening/ShipComplianceHistory.java | 45 + .../model/screening/ShipCountryCode.java | 25 + .../model/screening/ShipInfoMaster.java | 49 + .../screening/ShipRiskDetailHistory.java | 45 + .../BypassApiAccountRepository.java | 15 + .../repository/BypassApiConfigRepository.java | 31 + .../BypassApiRequestRepository.java | 13 + .../BypassApiServiceIpRepository.java | 11 + .../screening/ChangeTypeLangRepository.java | 14 + .../CompanyComplianceHistoryRepository.java | 12 + .../CompanyDetailInfoRepository.java | 12 + .../ComplianceCategoryLangRepository.java | 14 + .../ComplianceIndicatorLangRepository.java | 14 + .../ComplianceIndicatorRepository.java | 15 + .../MethodologyHistoryLangRepository.java | 14 + .../MethodologyHistoryRepository.java | 13 + .../RiskIndicatorCategoryLangRepository.java | 14 + .../RiskIndicatorLangRepository.java | 14 + .../screening/RiskIndicatorRepository.java | 13 + .../ShipComplianceHistoryRepository.java | 12 + .../screening/ShipCountryCodeRepository.java | 12 + .../screening/ShipInfoMasterRepository.java | 12 + .../ShipRiskDetailHistoryRepository.java | 113 + .../controller/ComplianceController.java | 37 + .../service/CompliancesByImosService.java | 32 + .../web/risk/controller/RiskController.java | 51 + .../web/risk/service/RisksByImosService.java | 32 + .../service/UpdatedComplianceListService.java | 33 + .../service/BypassApiAccountService.java | 218 + .../service/BypassApiRequestService.java | 205 + .../batch/service/BypassCodeGenerator.java | 385 ++ .../batch/service/BypassConfigService.java | 189 + .../com/snp/batch/service/EmailService.java | 84 + .../batch/service/ScreeningGuideService.java | 572 +++ src/main/resources/application-dev.yml | 56 + src/main/resources/application-prod.yml | 56 + src/main/resources/application.yml | 83 + .../db/schema/bypass_api_account.sql | 61 + src/main/resources/logback-spring.xml | 137 + .../templates/email/account-approved.html | 63 + .../templates/email/request-rejected.html | 50 + workflow-version.json | 176 + 176 files changed, 17768 insertions(+), 2 deletions(-) create mode 100644 .claude/settings.json create mode 100644 .claude/workflow-version.json create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitea/workflows/deploy.yml create mode 100755 .githooks/commit-msg create mode 100755 .githooks/post-checkout create mode 100755 .githooks/pre-commit create mode 100644 .gitignore create mode 100644 .mvn/settings.xml create mode 100644 .sdkmanrc create mode 100644 CLAUDE.md create mode 100644 frontend/.gitignore create mode 100644 frontend/README.md create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/public/android-chrome-192x192.png create mode 100644 frontend/public/android-chrome-512x512.png create mode 100644 frontend/public/apple-touch-icon.png create mode 100644 frontend/public/favicon-16x16.png create mode 100644 frontend/public/favicon-32x32.png create mode 100644 frontend/public/favicon.ico create mode 100644 frontend/public/site.webmanifest create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/api/bypassAccountApi.ts create mode 100644 frontend/src/api/bypassApi.ts create mode 100644 frontend/src/api/screeningGuideApi.ts create mode 100644 frontend/src/components/ConfirmModal.tsx create mode 100644 frontend/src/components/CopyButton.tsx create mode 100644 frontend/src/components/EmptyState.tsx create mode 100644 frontend/src/components/GuideModal.tsx create mode 100644 frontend/src/components/InfoItem.tsx create mode 100644 frontend/src/components/InfoModal.tsx create mode 100644 frontend/src/components/LoadingSpinner.tsx create mode 100644 frontend/src/components/Navbar.tsx create mode 100644 frontend/src/components/Pagination.tsx create mode 100644 frontend/src/components/StatusBadge.tsx create mode 100644 frontend/src/components/Toast.tsx create mode 100644 frontend/src/components/bypass/BypassConfigModal.tsx create mode 100644 frontend/src/components/bypass/BypassStepBasic.tsx create mode 100644 frontend/src/components/bypass/BypassStepParams.tsx create mode 100644 frontend/src/components/screening/ComplianceTab.tsx create mode 100644 frontend/src/components/screening/HistoryTab.tsx create mode 100644 frontend/src/components/screening/MethodologyTab.tsx create mode 100644 frontend/src/components/screening/RiskTab.tsx create mode 100644 frontend/src/constants/screeningTexts.ts create mode 100644 frontend/src/contexts/ThemeContext.tsx create mode 100644 frontend/src/contexts/ToastContext.tsx create mode 100644 frontend/src/hooks/usePoller.ts create mode 100644 frontend/src/hooks/useTheme.ts create mode 100644 frontend/src/hooks/useToast.ts create mode 100644 frontend/src/index.css create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/pages/BypassAccessRequest.tsx create mode 100644 frontend/src/pages/BypassAccountManagement.tsx create mode 100644 frontend/src/pages/BypassAccountRequests.tsx create mode 100644 frontend/src/pages/BypassCatalog.tsx create mode 100644 frontend/src/pages/BypassConfig.tsx create mode 100644 frontend/src/pages/MainMenu.tsx create mode 100644 frontend/src/pages/RiskComplianceHistory.tsx create mode 100644 frontend/src/pages/ScreeningGuide.tsx create mode 100644 frontend/src/theme/base.css create mode 100644 frontend/src/theme/tokens.css create mode 100644 frontend/src/utils/formatters.ts create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts create mode 100644 package-lock.json create mode 100644 pom.xml create mode 100644 src/main/java/com/snp/batch/SnpGlobalApplication.java create mode 100644 src/main/java/com/snp/batch/api/logging/ApiAccessLoggingFilter.java create mode 100644 src/main/java/com/snp/batch/common/web/ApiResponse.java create mode 100644 src/main/java/com/snp/batch/common/web/controller/BaseBypassController.java create mode 100644 src/main/java/com/snp/batch/common/web/service/BaseBypassService.java create mode 100644 src/main/java/com/snp/batch/global/config/BypassApiUserDetailsService.java create mode 100644 src/main/java/com/snp/batch/global/config/BypassAuthenticationEntryPoint.java create mode 100644 src/main/java/com/snp/batch/global/config/MaritimeApiWebClientConfig.java create mode 100644 src/main/java/com/snp/batch/global/config/SecurityConfig.java create mode 100644 src/main/java/com/snp/batch/global/config/SwaggerConfig.java create mode 100644 src/main/java/com/snp/batch/global/controller/BypassAccountController.java create mode 100644 src/main/java/com/snp/batch/global/controller/BypassConfigController.java create mode 100644 src/main/java/com/snp/batch/global/controller/ScreeningGuideController.java create mode 100644 src/main/java/com/snp/batch/global/controller/WebViewController.java create mode 100644 src/main/java/com/snp/batch/global/dto/BypassConfigRequest.java create mode 100644 src/main/java/com/snp/batch/global/dto/BypassConfigResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/BypassParamDto.java create mode 100644 src/main/java/com/snp/batch/global/dto/CodeGenerationResult.java create mode 100644 src/main/java/com/snp/batch/global/dto/bypass/BypassAccountResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/bypass/BypassAccountUpdateRequest.java create mode 100644 src/main/java/com/snp/batch/global/dto/bypass/BypassRequestResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/bypass/BypassRequestReviewRequest.java create mode 100644 src/main/java/com/snp/batch/global/dto/bypass/BypassRequestSubmitRequest.java create mode 100644 src/main/java/com/snp/batch/global/dto/bypass/ServiceIpDto.java create mode 100644 src/main/java/com/snp/batch/global/dto/screening/ChangeHistoryResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/screening/CompanyInfoResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/screening/ComplianceCategoryResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/screening/ComplianceIndicatorResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/screening/IndicatorStatusResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/screening/MethodologyHistoryResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/screening/RiskCategoryResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/screening/RiskIndicatorResponse.java create mode 100644 src/main/java/com/snp/batch/global/dto/screening/ShipInfoResponse.java create mode 100644 src/main/java/com/snp/batch/global/model/AccountStatus.java create mode 100644 src/main/java/com/snp/batch/global/model/BypassApiAccount.java create mode 100644 src/main/java/com/snp/batch/global/model/BypassApiConfig.java create mode 100644 src/main/java/com/snp/batch/global/model/BypassApiParam.java create mode 100644 src/main/java/com/snp/batch/global/model/BypassApiRequest.java create mode 100644 src/main/java/com/snp/batch/global/model/BypassApiServiceIp.java create mode 100644 src/main/java/com/snp/batch/global/model/RequestStatus.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ChangeType.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ChangeTypeLang.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ChangeTypeLangId.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/CompanyComplianceHistory.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/CompanyDetailInfo.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ComplianceCategory.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ComplianceCategoryLang.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ComplianceCategoryLangId.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ComplianceIndicator.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ComplianceIndicatorLang.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ComplianceIndicatorLangId.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/Language.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/MethodologyHistory.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/MethodologyHistoryLang.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/MethodologyHistoryLangId.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/RiskIndicator.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/RiskIndicatorCategory.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/RiskIndicatorCategoryLang.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/RiskIndicatorCategoryLangId.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/RiskIndicatorLang.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/RiskIndicatorLangId.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ShipComplianceHistory.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ShipCountryCode.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ShipInfoMaster.java create mode 100644 src/main/java/com/snp/batch/global/model/screening/ShipRiskDetailHistory.java create mode 100644 src/main/java/com/snp/batch/global/repository/BypassApiAccountRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/BypassApiConfigRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/BypassApiRequestRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/BypassApiServiceIpRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/ChangeTypeLangRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/CompanyComplianceHistoryRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/CompanyDetailInfoRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/ComplianceCategoryLangRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/ComplianceIndicatorLangRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/ComplianceIndicatorRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/MethodologyHistoryLangRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/MethodologyHistoryRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/RiskIndicatorCategoryLangRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/RiskIndicatorLangRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/RiskIndicatorRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/ShipComplianceHistoryRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/ShipCountryCodeRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/ShipInfoMasterRepository.java create mode 100644 src/main/java/com/snp/batch/global/repository/screening/ShipRiskDetailHistoryRepository.java create mode 100644 src/main/java/com/snp/batch/jobs/web/compliance/controller/ComplianceController.java create mode 100644 src/main/java/com/snp/batch/jobs/web/compliance/service/CompliancesByImosService.java create mode 100644 src/main/java/com/snp/batch/jobs/web/risk/controller/RiskController.java create mode 100644 src/main/java/com/snp/batch/jobs/web/risk/service/RisksByImosService.java create mode 100644 src/main/java/com/snp/batch/jobs/web/risk/service/UpdatedComplianceListService.java create mode 100644 src/main/java/com/snp/batch/service/BypassApiAccountService.java create mode 100644 src/main/java/com/snp/batch/service/BypassApiRequestService.java create mode 100644 src/main/java/com/snp/batch/service/BypassCodeGenerator.java create mode 100644 src/main/java/com/snp/batch/service/BypassConfigService.java create mode 100644 src/main/java/com/snp/batch/service/EmailService.java create mode 100644 src/main/java/com/snp/batch/service/ScreeningGuideService.java create mode 100644 src/main/resources/application-dev.yml create mode 100644 src/main/resources/application-prod.yml create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/db/schema/bypass_api_account.sql create mode 100644 src/main/resources/logback-spring.xml create mode 100644 src/main/resources/templates/email/account-approved.html create mode 100644 src/main/resources/templates/email/request-rejected.html create mode 100644 workflow-version.json diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..ec18b8e --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,87 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "env": { + "CLAUDE_BOT_TOKEN": "ac15488ad66463bd5c4e3be1fa6dd5b2743813c5" + }, + "permissions": { + "allow": [ + "Bash(./mvnw *)", + "Bash(curl -s *)", + "Bash(git add *)", + "Bash(git branch *)", + "Bash(git checkout *)", + "Bash(git commit *)", + "Bash(git config *)", + "Bash(git diff *)", + "Bash(git fetch *)", + "Bash(git log *)", + "Bash(git merge *)", + "Bash(git pull *)", + "Bash(git remote *)", + "Bash(git rev-parse *)", + "Bash(git show *)", + "Bash(git stash *)", + "Bash(git status)", + "Bash(git tag *)", + "Bash(java -jar *)", + "Bash(java -version)", + "Bash(mvn *)", + "Bash(sdk *)" + ], + "deny": [ + "Bash(git push --force*)", + "Bash(git push -f *)", + "Bash(git push origin --force*)", + "Bash(git reset --hard*)", + "Bash(git clean -fd*)", + "Bash(git checkout -- .)", + "Bash(git restore .)", + "Bash(rm -rf /)", + "Bash(rm -rf ~)", + "Bash(rm -rf .git*)", + "Bash(rm -rf /*)", + "Read(./**/.env)", + "Read(./**/.env.*)", + "Read(./**/secrets/**)", + "Read(./**/application-local.yml)", + "Read(./**/application-local.properties)" + ] + }, + "hooks": { + "SessionStart": [ + { + "matcher": "compact", + "hooks": [ + { + "type": "command", + "command": "bash .claude/scripts/on-post-compact.sh", + "timeout": 10 + } + ] + } + ], + "PreCompact": [ + { + "hooks": [ + { + "type": "command", + "command": "bash .claude/scripts/on-pre-compact.sh", + "timeout": 30 + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "bash .claude/scripts/on-commit.sh", + "timeout": 15 + } + ] + } + ] + } +} diff --git a/.claude/workflow-version.json b/.claude/workflow-version.json new file mode 100644 index 0000000..4220a0b --- /dev/null +++ b/.claude/workflow-version.json @@ -0,0 +1,6 @@ +{ + "applied_global_version": "1.6.1", + "applied_date": "2026-04-07", + "project_type": "java-maven", + "gitea_url": "https://gitea.gc-si.dev" +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6f831b5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,33 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{java,kt}] +indent_style = space +indent_size = 4 + +[*.{js,jsx,ts,tsx,json,yml,yaml,css,scss,html}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.{sh,bash}] +indent_style = space +indent_size = 4 + +[Makefile] +indent_style = tab + +[*.{gradle,groovy}] +indent_style = space +indent_size = 4 + +[*.xml] +indent_style = space +indent_size = 4 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2125666 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..0c8f923 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,49 @@ +name: Build and Deploy Batch + +on: + push: + branches: + - main + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + container: + image: maven:3.9-eclipse-temurin-17 + steps: + - name: Checkout + run: | + git clone --depth=1 --branch=${GITHUB_REF_NAME} \ + http://gitea:3000/${GITHUB_REPOSITORY}.git . + + - name: Configure Maven settings + run: | + mkdir -p ~/.m2 + cat > ~/.m2/settings.xml << 'SETTINGS' + + + + nexus + * + https://nexus.gc-si.dev/repository/maven-public/ + + + + + nexus + ${{ secrets.NEXUS_USERNAME }} + ${{ secrets.NEXUS_PASSWORD }} + + + + SETTINGS + + - name: Build + run: mvn clean package -DskipTests -B + + - name: Deploy + run: | + cp target/snp-batch-validation-*.jar /deploy/snp-batch/app.jar + date '+%Y-%m-%d %H:%M:%S' > /deploy/snp-batch/.deploy-trigger + echo "Deployed at $(cat /deploy/snp-batch/.deploy-trigger)" + ls -la /deploy/snp-batch/ diff --git a/.githooks/commit-msg b/.githooks/commit-msg new file mode 100755 index 0000000..93bb350 --- /dev/null +++ b/.githooks/commit-msg @@ -0,0 +1,60 @@ +#!/bin/bash +#============================================================================== +# commit-msg hook +# Conventional Commits 형식 검증 (한/영 혼용 지원) +#============================================================================== + +COMMIT_MSG_FILE="$1" +COMMIT_MSG=$(cat "$COMMIT_MSG_FILE") + +# Merge 커밋은 검증 건너뜀 +if echo "$COMMIT_MSG" | head -1 | grep -qE "^Merge "; then + exit 0 +fi + +# Revert 커밋은 검증 건너뜀 +if echo "$COMMIT_MSG" | head -1 | grep -qE "^Revert "; then + exit 0 +fi + +# Conventional Commits 정규식 +# type(scope): subject +# - type: feat|fix|docs|style|refactor|test|chore|ci|perf (필수) +# - scope: 영문, 숫자, 한글, 점, 밑줄, 하이픈 허용 (선택) +# - subject: 1~72자, 한/영 혼용 허용 (필수) +PATTERN='^(feat|fix|docs|style|refactor|test|chore|ci|perf)(\([a-zA-Z0-9가-힣._-]+\))?: .{1,72}$' + +FIRST_LINE=$(head -1 "$COMMIT_MSG_FILE") + +if ! echo "$FIRST_LINE" | grep -qE "$PATTERN"; then + echo "" + echo "╔══════════════════════════════════════════════════════════════╗" + echo "║ 커밋 메시지가 Conventional Commits 형식에 맞지 않습니다 ║" + echo "╚══════════════════════════════════════════════════════════════╝" + echo "" + echo " 올바른 형식: type(scope): subject" + echo "" + echo " type (필수):" + echo " feat — 새로운 기능" + echo " fix — 버그 수정" + echo " docs — 문서 변경" + echo " style — 코드 포맷팅" + echo " refactor — 리팩토링" + echo " test — 테스트" + echo " chore — 빌드/설정 변경" + echo " ci — CI/CD 변경" + echo " perf — 성능 개선" + echo "" + echo " scope (선택): 한/영 모두 가능" + echo " subject (필수): 1~72자, 한/영 모두 가능" + echo "" + echo " 예시:" + echo " feat(auth): JWT 기반 로그인 구현" + echo " fix(배치): 야간 배치 타임아웃 수정" + echo " docs: README 업데이트" + echo " chore: Gradle 의존성 업데이트" + echo "" + echo " 현재 메시지: $FIRST_LINE" + echo "" + exit 1 +fi diff --git a/.githooks/post-checkout b/.githooks/post-checkout new file mode 100755 index 0000000..bae360f --- /dev/null +++ b/.githooks/post-checkout @@ -0,0 +1,25 @@ +#!/bin/bash +#============================================================================== +# post-checkout hook +# 브랜치 체크아웃 시 core.hooksPath 자동 설정 +# clone/checkout 후 .githooks 디렉토리가 있으면 자동으로 hooksPath 설정 +#============================================================================== + +# post-checkout 파라미터: prev_HEAD, new_HEAD, branch_flag +# branch_flag=1: 브랜치 체크아웃, 0: 파일 체크아웃 +BRANCH_FLAG="$3" + +# 파일 체크아웃은 건너뜀 +if [ "$BRANCH_FLAG" = "0" ]; then + exit 0 +fi + +# .githooks 디렉토리 존재 확인 +REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) +if [ -d "${REPO_ROOT}/.githooks" ]; then + CURRENT_HOOKS_PATH=$(git config core.hooksPath 2>/dev/null || echo "") + if [ "$CURRENT_HOOKS_PATH" != ".githooks" ]; then + git config core.hooksPath .githooks + chmod +x "${REPO_ROOT}/.githooks/"* 2>/dev/null + fi +fi diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 0000000..7a1a3ec --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,33 @@ +#!/bin/bash +#============================================================================== +# pre-commit hook (Java Maven) +# Maven 컴파일 검증 — 컴파일 실패 시 커밋 차단 +#============================================================================== + +echo "pre-commit: Maven 컴파일 검증 중..." + +# Maven Wrapper 사용 (없으면 mvn 사용) +if [ -f "./mvnw" ]; then + MVN="./mvnw" +elif command -v mvn &>/dev/null; then + MVN="mvn" +else + echo "경고: Maven이 설치되지 않았습니다. 컴파일 검증을 건너뜁니다." + exit 0 +fi + +# 컴파일 검증 (테스트 제외, 오프라인 가능) +$MVN compile -q -DskipTests 2>&1 +RESULT=$? + +if [ $RESULT -ne 0 ]; then + echo "" + echo "╔══════════════════════════════════════════════════════════╗" + echo "║ 컴파일 실패! 커밋이 차단되었습니다. ║" + echo "║ 컴파일 오류를 수정한 후 다시 커밋해주세요. ║" + echo "╚══════════════════════════════════════════════════════════╝" + echo "" + exit 1 +fi + +echo "pre-commit: 컴파일 성공" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c93b022 --- /dev/null +++ b/.gitignore @@ -0,0 +1,120 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs +hs_err_pid* +replay_pid* + +# Maven +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.mvn/wrapper/maven-wrapper.properties +mvnw +mvnw.cmd + +# Gradle +.gradle/ +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +# IntelliJ IDEA +.idea/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +# Eclipse +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +# VS Code +.vscode/ + +# NetBeans +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +# Mac +.DS_Store + +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini + +# Application specific +application-local.yml +*.env +.env.* + +# Database +*.db +*.sqlite +*.sqlite3 + +# Logs +logs/ +*.log.* + +# Frontend (Vite + React) +frontend/node_modules/ +frontend/node/ +src/main/resources/static/ + +# Claude Code (개인 설정) +.claude/settings.local.json +.claude/CLAUDE.local.md +*.local + +# Team workflow (managed by /sync-team-workflow) +.claude/rules/ +.claude/agents/ +.claude/skills/push/ +.claude/skills/mr/ +.claude/skills/create-mr/ +.claude/skills/release/ +.claude/skills/version/ +.claude/skills/fix-issue/ +.claude/scripts/ diff --git a/.mvn/settings.xml b/.mvn/settings.xml new file mode 100644 index 0000000..e58dae0 --- /dev/null +++ b/.mvn/settings.xml @@ -0,0 +1,22 @@ + + + + + nexus + admin + Gcsc!8932 + + + + + + nexus + GC Nexus Repository + https://nexus.gc-si.dev/repository/maven-public/ + * + + + diff --git a/.sdkmanrc b/.sdkmanrc new file mode 100644 index 0000000..a4417cd --- /dev/null +++ b/.sdkmanrc @@ -0,0 +1 @@ +java=17.0.18-amzn diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8399263 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,104 @@ +# SNP-Global (snp-global) + +S&P Global Maritime API Gateway 및 Risk & Compliance Screening 시스템. 외부 Maritime API를 인증 기반으로 프록시(Bypass)하고, 선박/회사의 리스크·규정준수 스크리닝 데이터를 서비스. + +## 기술 스택 +- Java 17, Spring Boot 3.2.1 +- Spring Security (Basic Auth) +- PostgreSQL (스키마: std_snp_data) +- WebFlux WebClient (외부 Maritime API 호출) +- Spring Data JPA, JdbcTemplate +- Thymeleaf (이메일 템플릿) +- Springdoc OpenAPI 2.3.0 (Swagger) +- Lombok, Jackson +- React 19, TypeScript, Vite, Tailwind CSS (프론트엔드) + +## 빌드 & 실행 +```bash +# 빌드 +sdk use java 17.0.18-amzn +mvn clean package -DskipTests + +# 실행 +mvn spring-boot:run + +# 프론트엔드 개발 +cd frontend && npm install && npm run dev +``` + +## 서버 설정 +- 기본 포트: 8031 (dev/prod 프로파일: 8041) +- Context Path: /snp-global (dev/prod: /snp-api) +- Swagger UI: http://localhost:8031/snp-global/swagger-ui/index.html + +## 디렉토리 구조 +``` +src/main/java/com/snp/batch/ +├── SnpGlobalApplication.java # 메인 애플리케이션 +├── api/logging/ # API 접근 로깅 필터 +├── common/web/ # 공통 프레임워크 +│ ├── ApiResponse.java # 통합 API 응답 래퍼 +│ ├── controller/BaseBypassController # Bypass 컨트롤러 베이스 +│ └── service/BaseBypassService # Bypass 서비스 베이스 (WebClient) +├── global/ +│ ├── config/ # Security, Swagger, WebClient, Auth 설정 +│ ├── controller/ # Bypass 계정/설정 관리, Screening Guide, SPA 라우터 +│ ├── dto/ # bypass/, screening/ DTO +│ ├── model/ # bypass 엔티티, screening/ 다국어 엔티티 +│ └── repository/ # bypass, screening/ 리포지토리 +├── jobs/web/ # S&P API Bypass 엔드포인트 (자동 생성 가능) +│ ├── compliance/ # CompliancesByImos +│ └── risk/ # RisksByImos, UpdatedComplianceList +└── service/ # 핵심 비즈니스 서비스 + ├── BypassApiAccountService # 계정 CRUD + ├── BypassApiRequestService # 접근 요청 관리 (승인/거부) + ├── BypassCodeGenerator # 서비스/컨트롤러 코드 자동 생성 + ├── BypassConfigService # Bypass API 설정 CRUD + ├── EmailService # 이메일 발송 (승인/거부 알림) + └── ScreeningGuideService # 리스크·규정준수 스크리닝 데이터 조회 +``` + +## 주요 API 경로 + +### Bypass Account 관리 (/api/bypass-account) +| 메서드 | 경로 | 설명 | +|--------|------|------| +| GET | /accounts | 계정 목록 (페이징) | +| GET/PUT/DELETE | /accounts/{id} | 계정 상세/수정/삭제 | +| POST | /accounts/{id}/reset-password | 비밀번호 초기화 | +| POST | /requests | API 접근 요청 (공개) | +| GET | /requests | 요청 목록 | +| POST | /requests/{id}/approve | 승인 (계정 자동 생성) | +| POST | /requests/{id}/reject | 거부 | + +### Bypass Config 관리 (/api/bypass-config) +| 메서드 | 경로 | 설명 | +|--------|------|------| +| GET/POST | / | 설정 목록/생성 | +| GET/PUT/DELETE | /{id} | 설정 상세/수정/삭제 | +| POST | /{id}/generate | 서비스·컨트롤러 코드 생성 | + +### S&P API Bypass (/api/compliance, /api/risk) - Basic Auth 필요 +| 메서드 | 경로 | 설명 | +|--------|------|------| +| GET | /compliance/CompliancesByImos | IMO별 규정준수 데이터 | +| GET | /risk/RisksByImos | IMO별 리스크 데이터 | +| GET | /risk/UpdatedComplianceList | 기간별 업데이트 목록 | + +### Screening Guide (/api/screening-guide) +| 메서드 | 경로 | 설명 | +|--------|------|------| +| GET | /risk-indicators | 리스크 지표 (카테고리별) | +| GET | /compliance-indicators | 규정준수 지표 | +| GET | /methodology-history | 방법론 변경 이력 | +| GET | /history/ship-risk | 선박 리스크 변경 이력 | +| GET | /history/ship-compliance | 선박 규정준수 변경 이력 | +| GET | /history/company-compliance | 회사 규정준수 변경 이력 | +| GET | /ship-info, /company-info | 기본 정보 조회 | +| GET | /ship-risk-status | 선박 리스크 현황 | +| GET | /ship-compliance-status | 선박 규정준수 현황 | +| GET | /company-compliance-status | 회사 규정준수 현황 | + +## Lint/Format +- 별도 lint 도구 미설정 (checkstyle, spotless 없음) +- IDE 기본 포매터 사용 diff --git a/README.md b/README.md index 624f761..5b31905 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,105 @@ -# snp-global +# SNP-Batch (snp-batch-validation) -S&P Global API - Risk & Compliance 서비스 \ No newline at end of file +해양 데이터 통합 배치 시스템. Maritime API에서 선박/항만/사건 데이터를 수집하여 PostgreSQL에 저장하고, AIS 실시간 위치정보를 캐시 기반으로 서비스합니다. + +## 기술 스택 + +- Java 17, Spring Boot 3.2.1, Spring Batch 5.1.0 +- PostgreSQL, Quartz Scheduler, Caffeine Cache +- React 19 + Vite + Tailwind CSS 4 (관리 UI) +- frontend-maven-plugin (프론트엔드 빌드 통합) + +## 사전 요구사항 + +| 항목 | 버전 | 비고 | +|------|------|------| +| JDK | 17 | `.sdkmanrc` 참조 (`sdk env`) | +| Maven | 3.9+ | | +| Node.js | 20+ | 프론트엔드 빌드용 | +| npm | 10+ | Node.js에 포함 | + +## 빌드 + +> **주의**: frontend-maven-plugin의 Node 호환성 문제로, 프론트엔드와 백엔드를 분리하여 빌드합니다. + +### 터미널 + +```bash +# 1. 프론트엔드 빌드 +cd frontend && npm install && npm run build && cd .. + +# 2. Maven 패키징 (프론트엔드 빌드 스킵) +mvn clean package -DskipTests -Dskip.npm -Dskip.installnodenpm +``` + +빌드 결과: `target/snp-batch-validation-1.0.0.jar` + +### VSCode + +`Cmd+Shift+B` (기본 빌드 태스크) → 프론트엔드 빌드 + Maven 패키징 순차 실행 + +개별 태스크: `Cmd+Shift+P` → "Tasks: Run Task" → 태스크 선택 + +> 태스크 설정: [.vscode/tasks.json](.vscode/tasks.json) + +### IntelliJ IDEA + +1. **프론트엔드 빌드**: Terminal 탭에서 `cd frontend && npm run build` +2. **Maven 패키징**: Maven 패널 → Lifecycle → `package` + - VM Options: `-DskipTests -Dskip.npm -Dskip.installnodenpm` + - 또는 Run Configuration → Maven → Command line에 `clean package -DskipTests -Dskip.npm -Dskip.installnodenpm` + +## 로컬 실행 + +### 터미널 + +```bash +mvn spring-boot:run -Dspring-boot.run.profiles=local +``` + +### VSCode + +Run/Debug 패널(F5) → "SNP-Batch (local)" 선택 + +> 실행 설정: [.vscode/launch.json](.vscode/launch.json) + +### IntelliJ IDEA + +Run Configuration → Spring Boot: +- Main class: `com.snp.batch.SnpBatchApplication` +- Active profiles: `local` + +## 서버 배포 + +```bash +# 1. 빌드 (위 빌드 절차 수행) + +# 2. JAR 전송 +scp target/snp-batch-validation-1.0.0.jar {서버}:{경로}/ + +# 3. 실행 +java -jar snp-batch-validation-1.0.0.jar --spring.profiles.active=dev +``` + +## 접속 정보 + +| 항목 | URL | +|------|-----| +| 관리 UI | `http://localhost:8041/snp-api/` | +| Swagger | `http://localhost:8041/snp-api/swagger-ui/index.html` | + +## 프로파일 + +| 프로파일 | 용도 | DB | +|----------|------|----| +| `local` | 로컬 개발 | 개발 DB | +| `dev` | 개발 서버 | 개발 DB | +| `prod` | 운영 서버 | 운영 DB | + +## Maven 빌드 플래그 요약 + +| 플래그 | 용도 | +|--------|------| +| `-DskipTests` | 테스트 스킵 | +| `-Dskip.npm` | npm install/build 스킵 | +| `-Dskip.installnodenpm` | Node/npm 자동 설치 스킵 | diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# 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? diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..d2e7761 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + 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/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) 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 + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..af1322b --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,17 @@ + + + + + + + + + + + S&P Global API + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..46883a7 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,3935 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-router-dom": "^7.13.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@tailwindcss/vite": "^4.1.18", + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "tailwindcss": "^4.1.18", + "typescript": "~5.9.3", + "typescript-eslint": "^8.48.0", + "vite": "^7.3.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "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.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.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.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@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.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "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.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "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.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.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.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "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.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", + "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "enhanced-resolve": "^5.18.3", + "jiti": "^2.6.1", + "lightningcss": "1.30.2", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.18" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", + "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-x64": "4.1.18", + "@tailwindcss/oxide-freebsd-x64": "4.1.18", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-x64-musl": "4.1.18", + "@tailwindcss/oxide-wasm32-wasi": "4.1.18", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", + "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", + "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", + "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", + "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", + "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", + "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", + "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", + "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", + "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", + "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.0", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", + "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", + "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.18.tgz", + "integrity": "sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.18", + "@tailwindcss/oxide": "4.1.18", + "tailwindcss": "4.1.18" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7" + } + }, + "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.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.10.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.13.tgz", + "integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", + "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/type-utils": "8.56.0", + "@typescript-eslint/utils": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.56.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", + "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz", + "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.56.0", + "@typescript-eslint/types": "^8.56.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz", + "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz", + "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz", + "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/utils": "8.56.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz", + "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz", + "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.56.0", + "@typescript-eslint/tsconfig-utils": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz", + "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz", + "integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.56.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", + "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", + "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.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/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/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "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.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "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": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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.30001770", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", + "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", + "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/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/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.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/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/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "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.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "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.26", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz", + "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==", + "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.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "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.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/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/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/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/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.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/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.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "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.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", + "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==", + "dev": true, + "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-android-arm64": "1.30.2", + "lightningcss-darwin-arm64": "1.30.2", + "lightningcss-darwin-x64": "1.30.2", + "lightningcss-freebsd-x64": "1.30.2", + "lightningcss-linux-arm-gnueabihf": "1.30.2", + "lightningcss-linux-arm64-gnu": "1.30.2", + "lightningcss-linux-arm64-musl": "1.30.2", + "lightningcss-linux-x64-gnu": "1.30.2", + "lightningcss-linux-x64-musl": "1.30.2", + "lightningcss-win32-arm64-msvc": "1.30.2", + "lightningcss-win32-x64-msvc": "1.30.2" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", + "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz", + "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", + "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", + "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", + "cpu": [ + "x64" + ], + "dev": true, + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", + "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", + "cpu": [ + "arm" + ], + "dev": true, + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", + "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", + "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", + "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", + "cpu": [ + "x64" + ], + "dev": true, + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", + "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", + "cpu": [ + "x64" + ], + "dev": true, + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", + "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", + "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", + "cpu": [ + "x64" + ], + "dev": true, + "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/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/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.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/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==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/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/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.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.0.tgz", + "integrity": "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==", + "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/react-router-dom": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.0.tgz", + "integrity": "sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g==", + "license": "MIT", + "dependencies": { + "react-router": "7.13.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "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.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "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.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "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==", + "dev": true, + "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/tailwindcss": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "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/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.0.tgz", + "integrity": "sha512-c7toRLrotJ9oixgdW7liukZpsnq5CZ7PuKztubGYlNppuTqhIoWfhgHo/7EU0v06gS2l/x0i2NEFK1qMIf0rIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.56.0", + "@typescript-eslint/parser": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/utils": "8.56.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "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/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/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" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..7204e4d --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,33 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-router-dom": "^7.13.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@tailwindcss/vite": "^4.1.18", + "@types/node": "^24.10.1", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "tailwindcss": "^4.1.18", + "typescript": "~5.9.3", + "typescript-eslint": "^8.48.0", + "vite": "^7.3.1" + } +} diff --git a/frontend/public/android-chrome-192x192.png b/frontend/public/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..8755e9a9ae13d29c805e461b8c968fae2912727c GIT binary patch literal 12478 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 literal 0 HcmV?d00001 diff --git a/frontend/public/android-chrome-512x512.png b/frontend/public/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..de622e9904c82f680bed07473722d792d309f0d8 GIT binary patch literal 26995 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 BxE%lh literal 0 HcmV?d00001 diff --git a/frontend/public/apple-touch-icon.png b/frontend/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..834a3a8909e0d4593be44d6983c2062574ccaf1e GIT binary patch literal 11070 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 literal 0 HcmV?d00001 diff --git a/frontend/public/favicon-32x32.png b/frontend/public/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..20eedf0aa5d117560fefe59f056e7692a63aadff GIT binary patch literal 1643 zcmV-x29)`UP)_9H$XcK+*)%AY};=dGG%_=RV%^J$NYNz@2mMJ@@SYz4x5=WVCR&Yqd_E zeu1H0egWTj`UPAeEcf&qq%&Xu(E(QsvcOQ*ZJ}GdGyxxsvPxjr| z-Xa0cQj&DPl9CHyYl92><{0LVO$qQl=_KVa3ELt#xfS5TF*ugk(PT=Xx=vo%y2no5 zt%B-rh-DCp1h`!2u|yeIZ9j7?j>(Zk0#q&wX=C-Zck1Mfz(E5sY)Bw_bnixLF9v)2 z4$ux9fOm!shDV>?kR-{Pfn7sP$PHaLbsa{Hd=F2b*5Sm-(>R)S93`dq;qT{zEt`_y>*HlmrdgI{tXZ`bp;JQWi-9v| zFW|`0UyxsL8$G(aA!%*AI1y4c2Mx!TB`71zxlD)82ouN04km?$BW_s&R;^CPCt)*@ zb?gK*?b~C~f_bbeFnHWY7)n?4@L?IIO^-zE;^jzKyB?o~&qC^^ZLqPC5j%e_Y;A3Y zzH#E%GWGAbjSODx>})Z9+-Rt(ish>k@#xWGW((BmbXb?N5mnXI@b>aR@1Ac7{-lY) zVofC3-}j0IZJaXp9uz0hwW|yI_4PBd#mcc|qIwaUz@v{lI(D>2L17V!itd>BMxwU% zDaMCl6Rdgi1QivPc=oKmkxX)? z1Yj|whMfvJpazT0&fR-(a&pArApW1i5=-2cmV)1o9m0Tq&rhsD z;|`S}uEA{7*ViNSr(@!4xDj>k+{q-BBnc5zqONbgfnvJz!rMhccHmH^SYxB3;O6Ek zXyY_FBv@2TIi2jE*K>_*vC?^6qcW6Fik;b%zH=9f{<=%2;DP8l7P2p21t*sA-Bu*T$00U)E_QC;i23toiN~3kvI+I|I-zfz zRLCb~y+;2tF=`1-z^^uPZVZl}I0X%L#gXq1$AsW9@bqv8S4VX05?svAG3j$c3#j?* zJD4f1XTr%s>4t^7hL)FFkSnW?(^KU|{IIYm`-^Hu>A>%F1AA*SJP z`OTZz`I0Q9HsiC^*%HO#nCw`b022=Fl%;@V`ApFkL&`P8GWa$nkdu@1#8#Cj5WAeP zt)QG^a4cg0Qv%G$%ez#ds8&@Lq8r2n$!z3V`WI2TNGbrf+Mi7l0gG>%%UAAFcykz_j p009600#`#Y00006Nkljz=&iSp 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 literal 0 HcmV?d00001 diff --git a/frontend/public/site.webmanifest b/frontend/public/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/frontend/public/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..d7dffa0 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,69 @@ +import { lazy, Suspense } from 'react'; +import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom'; +import { ToastProvider, useToastContext } from './contexts/ToastContext'; +import { ThemeProvider } from './contexts/ThemeContext'; +import Navbar from './components/Navbar'; +import ToastContainer from './components/Toast'; +import LoadingSpinner from './components/LoadingSpinner'; + +const MainMenu = lazy(() => import('./pages/MainMenu')); +const BypassConfig = lazy(() => import('./pages/BypassConfig')); +const BypassCatalog = lazy(() => import('./pages/BypassCatalog')); +const ScreeningGuide = lazy(() => import('./pages/ScreeningGuide')); +const RiskComplianceHistory = lazy(() => import('./pages/RiskComplianceHistory')); +const BypassAccountRequests = lazy(() => import('./pages/BypassAccountRequests')); +const BypassAccountManagement = lazy(() => import('./pages/BypassAccountManagement')); +const BypassAccessRequest = lazy(() => import('./pages/BypassAccessRequest')); + +function AppLayout() { + const { toasts, removeToast } = useToastContext(); + const location = useLocation(); + const isMainMenu = location.pathname === '/'; + + return ( +
+ {/* 메인 화면: 전체화면, 섹션 페이지: 탭 + 스크롤 콘텐츠 */} + {isMainMenu ? ( +
+ }> + + } /> + + +
+ ) : ( + <> +
+ +
+
+ }> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + +
+ + )} + +
+ ); +} + +export default function App() { + return ( + + + + + + + + ); +} diff --git a/frontend/src/api/bypassAccountApi.ts b/frontend/src/api/bypassAccountApi.ts new file mode 100644 index 0000000..072396f --- /dev/null +++ b/frontend/src/api/bypassAccountApi.ts @@ -0,0 +1,161 @@ +export interface BypassAccountResponse { + id: number; + username: string; + displayName: string; + organization: string | null; + projectName: string | null; + email: string | null; + phone: string | null; + status: string; + accessStartDate: string | null; + accessEndDate: string | null; + createdAt: string; + updatedAt: string; + plainPassword: string | null; + serviceIps: string | null; +} + +export interface BypassAccountUpdateRequest { + displayName?: string; + organization?: string; + email?: string; + phone?: string; + status?: string; + accessStartDate?: string; + accessEndDate?: string; +} + +export interface BypassRequestResponse { + id: number; + applicantName: string; + organization: string | null; + purpose: string | null; + email: string | null; + phone: string | null; + requestedAccessPeriod: string | null; + status: string; + reviewedBy: string | null; + reviewedAt: string | null; + rejectReason: string | null; + accountId: number | null; + accountUsername: string | null; + createdAt: string; + updatedAt: string; + projectName: string | null; + expectedCallVolume: string | null; + serviceIps: string | null; +} + +export interface BypassRequestSubmitRequest { + applicantName: string; + organization: string; + purpose: string; + email: string; + phone: string; + requestedAccessPeriod: string; + projectName: string; + expectedCallVolume: string; + serviceIps: string; // JSON string +} + +export interface BypassRequestReviewRequest { + reviewedBy: string; + rejectReason?: string; + accessStartDate?: string; + accessEndDate?: string; +} + +export interface PageResponse { + content: T[]; + totalElements: number; + totalPages: number; + number: number; + size: number; +} + +interface ApiResponse { + success: boolean; + message: string; + data: T; +} + +export interface ServiceIpDto { + ip: string; + purpose: string; + expectedCallVolume: string; + description: string; +} + +// BASE URL +const BASE = '/snp-global/api/bypass-account'; + +// 헬퍼 함수 (bypassApi.ts 패턴과 동일) +async function fetchJson(url: string): Promise { + const res = await fetch(url); + if (!res.ok) throw new Error(`API Error: ${res.status} ${res.statusText}`); + return res.json(); +} + +async function postJson(url: string, body?: unknown): Promise { + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: body != null ? JSON.stringify(body) : undefined, + }); + if (!res.ok) throw new Error(`API Error: ${res.status} ${res.statusText}`); + return res.json(); +} + +async function putJson(url: string, body?: unknown): Promise { + const res = await fetch(url, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: body != null ? JSON.stringify(body) : undefined, + }); + if (!res.ok) throw new Error(`API Error: ${res.status} ${res.statusText}`); + return res.json(); +} + +async function deleteJson(url: string): Promise { + const res = await fetch(url, { method: 'DELETE' }); + if (!res.ok) throw new Error(`API Error: ${res.status} ${res.statusText}`); + return res.json(); +} + +export const bypassAccountApi = { + // Accounts + getAccounts: (status?: string, page = 0, size = 20) => { + const params = new URLSearchParams({ page: String(page), size: String(size) }); + if (status) params.set('status', status); + return fetchJson>>(`${BASE}/accounts?${params}`); + }, + getAccount: (id: number) => + fetchJson>(`${BASE}/accounts/${id}`), + updateAccount: (id: number, data: BypassAccountUpdateRequest) => + putJson>(`${BASE}/accounts/${id}`, data), + deleteAccount: (id: number) => + deleteJson>(`${BASE}/accounts/${id}`), + resetPassword: (id: number) => + postJson>(`${BASE}/accounts/${id}/reset-password`, {}), + getAccountIps: (accountId: number) => + fetchJson>(`${BASE}/accounts/${accountId}/ips`), + addAccountIp: (accountId: number, data: ServiceIpDto) => + postJson>(`${BASE}/accounts/${accountId}/ips`, data), + deleteAccountIp: (accountId: number, ipId: number) => + deleteJson>(`${BASE}/accounts/${accountId}/ips/${ipId}`), + + // Requests + submitRequest: (data: BypassRequestSubmitRequest) => + postJson>(`${BASE}/requests`, data), + getRequests: (status?: string, page = 0, size = 20) => { + const params = new URLSearchParams({ page: String(page), size: String(size) }); + if (status) params.set('status', status); + return fetchJson>>(`${BASE}/requests?${params}`); + }, + approveRequest: (id: number, data: BypassRequestReviewRequest) => + postJson>(`${BASE}/requests/${id}/approve`, data), + rejectRequest: (id: number, data: BypassRequestReviewRequest) => + postJson>(`${BASE}/requests/${id}/reject`, data), + reopenRequest: (id: number) => + postJson>(`${BASE}/requests/${id}/reopen`, {}), +}; diff --git a/frontend/src/api/bypassApi.ts b/frontend/src/api/bypassApi.ts new file mode 100644 index 0000000..1fc943d --- /dev/null +++ b/frontend/src/api/bypassApi.ts @@ -0,0 +1,109 @@ +// API 응답 타입 +interface ApiResponse { + success: boolean; + message: string; + data: T; + errorCode?: string; +} + +// 타입 정의 +export interface BypassParamDto { + id?: number; + paramName: string; + paramType: string; // STRING, INTEGER, LONG, BOOLEAN + paramIn: string; // PATH, QUERY, BODY + required: boolean; + description: string; + example: string; // Swagger @Parameter example 값 + sortOrder: number; +} + +export interface BypassConfigRequest { + domainName: string; + displayName: string; + webclientBean: string; + externalPath: string; + httpMethod: string; + description: string; + params: BypassParamDto[]; +} + +export interface BypassConfigResponse { + id: number; + domainName: string; + endpointName: string; + displayName: string; + webclientBean: string; + externalPath: string; + httpMethod: string; + description: string; + generated: boolean; + generatedAt: string | null; + createdAt: string; + updatedAt: string; + params: BypassParamDto[]; +} + +export interface CodeGenerationResult { + controllerPath: string; + servicePaths: string[]; + message: string; +} + +export interface WebClientBeanInfo { + name: string; + description: string; +} + +// BASE URL +const BASE = '/snp-global/api/bypass-config'; + +// 헬퍼 함수 (batchApi.ts 패턴과 동일) +async function fetchJson(url: string): Promise { + const res = await fetch(url); + if (!res.ok) throw new Error(`API Error: ${res.status} ${res.statusText}`); + return res.json(); +} + +async function postJson(url: string, body?: unknown): Promise { + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: body != null ? JSON.stringify(body) : undefined, + }); + if (!res.ok) throw new Error(`API Error: ${res.status} ${res.statusText}`); + return res.json(); +} + +async function putJson(url: string, body?: unknown): Promise { + const res = await fetch(url, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: body != null ? JSON.stringify(body) : undefined, + }); + if (!res.ok) throw new Error(`API Error: ${res.status} ${res.statusText}`); + return res.json(); +} + +async function deleteJson(url: string): Promise { + const res = await fetch(url, { method: 'DELETE' }); + if (!res.ok) throw new Error(`API Error: ${res.status} ${res.statusText}`); + return res.json(); +} + +export const bypassApi = { + getConfigs: () => + fetchJson>(BASE), + getConfig: (id: number) => + fetchJson>(`${BASE}/${id}`), + createConfig: (data: BypassConfigRequest) => + postJson>(BASE, data), + updateConfig: (id: number, data: BypassConfigRequest) => + putJson>(`${BASE}/${id}`, data), + deleteConfig: (id: number) => + deleteJson>(`${BASE}/${id}`), + generateCode: (id: number, force = false) => + postJson>(`${BASE}/${id}/generate?force=${force}`), + getWebclientBeans: () => + fetchJson>(`${BASE}/webclient-beans`), +}; diff --git a/frontend/src/api/screeningGuideApi.ts b/frontend/src/api/screeningGuideApi.ts new file mode 100644 index 0000000..8994489 --- /dev/null +++ b/frontend/src/api/screeningGuideApi.ts @@ -0,0 +1,149 @@ +// API 응답 타입 +interface ApiResponse { + success: boolean; + message: string; + data: T; +} + +// Risk 지표 타입 +export interface RiskIndicatorResponse { + indicatorId: number; + fieldKey: string; + fieldName: string; + description: string; + conditionRed: string; + conditionAmber: string; + conditionGreen: string; + dataType: string; +} + +export interface RiskCategoryResponse { + categoryCode: string; + categoryName: string; + indicators: RiskIndicatorResponse[]; +} + +// Compliance 지표 타입 +export interface ComplianceIndicatorResponse { + indicatorId: number; + fieldKey: string; + fieldName: string; + description: string; + conditionRed: string; + conditionAmber: string; + conditionGreen: string; + dataType: string; +} + +export interface ComplianceCategoryResponse { + categoryCode: string; + categoryName: string; + indicatorType: string; + indicators: ComplianceIndicatorResponse[]; +} + +// 방법론 변경 이력 타입 +export interface MethodologyHistoryResponse { + historyId: number; + changeDate: string; + changeTypeCode: string; + changeType: string; + description: string; +} + +// 값 변경 이력 타입 +export interface ChangeHistoryResponse { + rowIndex: number; + searchKey: string; + lastModifiedDate: string; + changedColumnName: string; + beforeValue: string; + afterValue: string; + fieldName: string; + narrative: string; + prevNarrative: string; + sortOrder: number; +} + +// 선박 기본 정보 +export interface ShipInfoResponse { + imoNo: string; + shipName: string; + shipStatus: string; + nationalityCode: string; + nationalityIsoCode: string | null; + nationality: string; + shipType: string; + dwt: string; + gt: string; + buildYear: string; + mmsiNo: string; + callSign: string; + shipTypeGroup: string; +} + +// 회사 기본 정보 +export interface CompanyInfoResponse { + companyCode: string; + fullName: string; + abbreviation: string; + status: string; + parentCompanyCode: string | null; + parentCompanyName: string | null; + registrationCountry: string; + registrationCountryCode: string; + registrationCountryIsoCode: string | null; + controlCountry: string | null; + controlCountryCode: string | null; + controlCountryIsoCode: string | null; + foundedDate: string | null; + email: string | null; + phone: string | null; + website: string | null; +} + +// 지표 현재 상태 +export interface IndicatorStatusResponse { + columnName: string; + fieldName: string; + categoryCode: string; + category: string; + value: string | null; + narrative: string | null; + sortOrder: number; +} + +const BASE = '/snp-global/api/screening-guide'; + +async function fetchJson(url: string): Promise { + const res = await fetch(url); + if (!res.ok) throw new Error(`API Error: ${res.status}`); + return res.json(); +} + +export const screeningGuideApi = { + getRiskIndicators: (lang = 'KO') => + fetchJson>(`${BASE}/risk-indicators?lang=${lang}`), + getComplianceIndicators: (lang = 'KO', type = 'SHIP') => + fetchJson>(`${BASE}/compliance-indicators?lang=${lang}&type=${type}`), + getMethodologyHistory: (lang = 'KO') => + fetchJson>(`${BASE}/methodology-history?lang=${lang}`), + getMethodologyBanner: (lang = 'KO') => + fetchJson>(`${BASE}/methodology-banner?lang=${lang}`), + getShipRiskHistory: (imoNo: string, lang = 'KO') => + fetchJson>(`${BASE}/history/ship-risk?imoNo=${imoNo}&lang=${lang}`), + getShipComplianceHistory: (imoNo: string, lang = 'KO') => + fetchJson>(`${BASE}/history/ship-compliance?imoNo=${imoNo}&lang=${lang}`), + getCompanyComplianceHistory: (companyCode: string, lang = 'KO') => + fetchJson>(`${BASE}/history/company-compliance?companyCode=${companyCode}&lang=${lang}`), + getShipInfo: (imoNo: string) => + fetchJson>(`${BASE}/ship-info?imoNo=${imoNo}`), + getShipRiskStatus: (imoNo: string, lang = 'KO') => + fetchJson>(`${BASE}/ship-risk-status?imoNo=${imoNo}&lang=${lang}`), + getShipComplianceStatus: (imoNo: string, lang = 'KO') => + fetchJson>(`${BASE}/ship-compliance-status?imoNo=${imoNo}&lang=${lang}`), + getCompanyInfo: (companyCode: string) => + fetchJson>(`${BASE}/company-info?companyCode=${companyCode}`), + getCompanyComplianceStatus: (companyCode: string, lang = 'KO') => + fetchJson>(`${BASE}/company-compliance-status?companyCode=${companyCode}&lang=${lang}`), +}; diff --git a/frontend/src/components/ConfirmModal.tsx b/frontend/src/components/ConfirmModal.tsx new file mode 100644 index 0000000..8ae36f5 --- /dev/null +++ b/frontend/src/components/ConfirmModal.tsx @@ -0,0 +1,49 @@ +interface Props { + open: boolean; + title?: string; + message: string; + confirmLabel?: string; + cancelLabel?: string; + confirmColor?: string; + onConfirm: () => void; + onCancel: () => void; +} + +export default function ConfirmModal({ + open, + title = '확인', + message, + confirmLabel = '확인', + cancelLabel = '취소', + confirmColor = 'bg-wing-accent hover:bg-wing-accent/80', + onConfirm, + onCancel, +}: Props) { + if (!open) return null; + + return ( +
+
e.stopPropagation()} + > +

{title}

+

{message}

+
+ + +
+
+
+ ); +} diff --git a/frontend/src/components/CopyButton.tsx b/frontend/src/components/CopyButton.tsx new file mode 100644 index 0000000..617bf3e --- /dev/null +++ b/frontend/src/components/CopyButton.tsx @@ -0,0 +1,48 @@ +import { useState } from 'react'; + +interface CopyButtonProps { + text: string; +} + +export default function CopyButton({ text }: CopyButtonProps) { + const [copied, setCopied] = useState(false); + + const handleCopy = async (e: React.MouseEvent) => { + e.stopPropagation(); + try { + await navigator.clipboard.writeText(text); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + } catch { + const textarea = document.createElement('textarea'); + textarea.value = text; + textarea.style.position = 'fixed'; + textarea.style.opacity = '0'; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + } + }; + + return ( + + ); +} diff --git a/frontend/src/components/EmptyState.tsx b/frontend/src/components/EmptyState.tsx new file mode 100644 index 0000000..0cf5f0a --- /dev/null +++ b/frontend/src/components/EmptyState.tsx @@ -0,0 +1,15 @@ +interface Props { + icon?: string; + message: string; + sub?: string; +} + +export default function EmptyState({ icon = '📭', message, sub }: Props) { + return ( +
+ {icon} +

{message}

+ {sub &&

{sub}

} +
+ ); +} diff --git a/frontend/src/components/GuideModal.tsx b/frontend/src/components/GuideModal.tsx new file mode 100644 index 0000000..0c4ee2e --- /dev/null +++ b/frontend/src/components/GuideModal.tsx @@ -0,0 +1,92 @@ +import { useState } from 'react'; + +interface GuideSection { + title: string; + content: string; +} + +interface Props { + open: boolean; + pageTitle: string; + sections: GuideSection[]; + onClose: () => void; +} + +export default function GuideModal({ open, pageTitle, sections, onClose }: Props) { + if (!open) return null; + + return ( +
+
e.stopPropagation()} + > +
+

{pageTitle} 사용 가이드

+ +
+ +
+ {sections.map((section, i) => ( + + ))} +
+ +
+ +
+
+
+ ); +} + +function GuideAccordion({ title, content, defaultOpen }: { title: string; content: string; defaultOpen: boolean }) { + const [isOpen, setIsOpen] = useState(defaultOpen); + + return ( +
+ + {isOpen && ( +
+ {content} +
+ )} +
+ ); +} + +export function HelpButton({ onClick }: { onClick: () => void }) { + return ( + + ); +} diff --git a/frontend/src/components/InfoItem.tsx b/frontend/src/components/InfoItem.tsx new file mode 100644 index 0000000..d8b400e --- /dev/null +++ b/frontend/src/components/InfoItem.tsx @@ -0,0 +1,15 @@ +interface InfoItemProps { + label: string; + value: string; +} + +export default function InfoItem({ label, value }: InfoItemProps) { + return ( +
+
+ {label} +
+
{value || '-'}
+
+ ); +} diff --git a/frontend/src/components/InfoModal.tsx b/frontend/src/components/InfoModal.tsx new file mode 100644 index 0000000..cedb5a9 --- /dev/null +++ b/frontend/src/components/InfoModal.tsx @@ -0,0 +1,35 @@ +interface Props { + open: boolean; + title?: string; + children: React.ReactNode; + onClose: () => void; +} + +export default function InfoModal({ + open, + title = '정보', + children, + onClose, +}: Props) { + if (!open) return null; + + return ( +
+
e.stopPropagation()} + > +

{title}

+
{children}
+
+ +
+
+
+ ); +} diff --git a/frontend/src/components/LoadingSpinner.tsx b/frontend/src/components/LoadingSpinner.tsx new file mode 100644 index 0000000..738e225 --- /dev/null +++ b/frontend/src/components/LoadingSpinner.tsx @@ -0,0 +1,7 @@ +export default function LoadingSpinner({ className = '' }: { className?: string }) { + return ( +
+
+
+ ); +} diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx new file mode 100644 index 0000000..ab6f99f --- /dev/null +++ b/frontend/src/components/Navbar.tsx @@ -0,0 +1,137 @@ +import { Link, useLocation, useNavigate } from 'react-router-dom'; +import { useThemeContext } from '../contexts/ThemeContext'; + +interface MenuItem { + id: string; + label: string; + path: string; +} + +interface MenuSection { + id: string; + label: string; + shortLabel: string; + icon: React.ReactNode; + defaultPath: string; + children: MenuItem[]; +} + +const MENU_STRUCTURE: MenuSection[] = [ + { + id: 'bypass', + label: 'S&P Global API', + shortLabel: 'S&P Global API', + icon: ( + + + + ), + defaultPath: '/bypass-catalog', + children: [ + { id: 'bypass-catalog', label: 'API 카탈로그', path: '/bypass-catalog' }, + { id: 'bypass-config', label: 'API 관리', path: '/bypass-config' }, + { id: 'bypass-account-requests', label: '계정 신청 관리', path: '/bypass-account-requests' }, + { id: 'bypass-account-management', label: '계정 관리', path: '/bypass-account-management' }, + { id: 'bypass-access-request', label: 'API 계정 신청', path: '/bypass-access-request' }, + ], + }, + { + id: 'risk', + label: 'S&P Risk & Compliance', + shortLabel: 'Risk & Compliance', + icon: ( + + + + ), + defaultPath: '/risk-compliance-history', + children: [ + { id: 'risk-compliance-history', label: 'Change History', path: '/risk-compliance-history' }, + { id: 'screening-guide', label: 'Screening Guide', path: '/screening-guide' }, + ], + }, +]; + +function getCurrentSection(pathname: string): MenuSection | null { + for (const section of MENU_STRUCTURE) { + if (section.children.some((c) => pathname === c.path || pathname.startsWith(c.path + '/'))) { + return section; + } + } + return null; +} + +export default function Navbar() { + const location = useLocation(); + const navigate = useNavigate(); + const { theme, toggle } = useThemeContext(); + const currentSection = getCurrentSection(location.pathname); + + // 메인 화면에서는 숨김 + if (!currentSection) return null; + + const isActivePath = (path: string) => { + return location.pathname === path || location.pathname.startsWith(path + '/'); + }; + + return ( +
+ {/* 1단: 섹션 탭 */} +
+ + ← + +
+ {MENU_STRUCTURE.map((section) => ( + + ))} +
+ +
+ + {/* 2단: 서브 탭 */} +
+
+ {currentSection?.children.map((child) => ( + + ))} +
+
+
+ ); +} diff --git a/frontend/src/components/Pagination.tsx b/frontend/src/components/Pagination.tsx new file mode 100644 index 0000000..b735e4f --- /dev/null +++ b/frontend/src/components/Pagination.tsx @@ -0,0 +1,145 @@ +interface PaginationProps { + page: number; + totalPages: number; + totalElements: number; + pageSize: number; + onPageChange: (page: number) => void; +} + +/** + * 표시할 페이지 번호 목록 생성 (Truncated Page Number) + * - 총 7슬롯 이하면 전부 표시 + * - 7슬롯 초과면 현재 페이지 기준 양쪽 1개 + 처음/끝 + ellipsis + */ +function getPageNumbers(current: number, total: number): (number | 'ellipsis')[] { + if (total <= 7) { + return Array.from({ length: total }, (_, i) => i); + } + + const pages: (number | 'ellipsis')[] = []; + const SIBLING = 1; + + const leftSibling = Math.max(current - SIBLING, 0); + const rightSibling = Math.min(current + SIBLING, total - 1); + + const showLeftEllipsis = leftSibling > 1; + const showRightEllipsis = rightSibling < total - 2; + + pages.push(0); + + if (showLeftEllipsis) { + pages.push('ellipsis'); + } else { + for (let i = 1; i < leftSibling; i++) { + pages.push(i); + } + } + + for (let i = leftSibling; i <= rightSibling; i++) { + if (i !== 0 && i !== total - 1) { + pages.push(i); + } + } + + if (showRightEllipsis) { + pages.push('ellipsis'); + } else { + for (let i = rightSibling + 1; i < total - 1; i++) { + pages.push(i); + } + } + + if (total > 1) { + pages.push(total - 1); + } + + return pages; +} + +export default function Pagination({ + page, + totalPages, + totalElements, + pageSize, + onPageChange, +}: PaginationProps) { + if (totalPages <= 1) return null; + + const start = page * pageSize + 1; + const end = Math.min((page + 1) * pageSize, totalElements); + const pages = getPageNumbers(page, totalPages); + + const btnBase = + 'inline-flex items-center justify-center w-7 h-7 text-xs rounded transition-colors'; + const btnEnabled = 'hover:bg-wing-hover text-wing-muted'; + const btnDisabled = 'opacity-30 cursor-not-allowed text-wing-muted'; + + return ( +
+ + {totalElements.toLocaleString()}건 중 {start.toLocaleString()}~ + {end.toLocaleString()} + +
+ {/* First */} + + {/* Prev */} + + + {/* Page Numbers */} + {pages.map((p, idx) => + p === 'ellipsis' ? ( + + … + + ) : ( + + ), + )} + + {/* Next */} + + {/* Last */} + +
+
+ ); +} diff --git a/frontend/src/components/StatusBadge.tsx b/frontend/src/components/StatusBadge.tsx new file mode 100644 index 0000000..686660e --- /dev/null +++ b/frontend/src/components/StatusBadge.tsx @@ -0,0 +1,40 @@ +const STATUS_CONFIG: Record = { + COMPLETED: { bg: 'bg-emerald-100 text-emerald-700', text: '완료', label: '✓' }, + FAILED: { bg: 'bg-red-100 text-red-700', text: '실패', label: '✕' }, + STARTED: { bg: 'bg-blue-100 text-blue-700', text: '실행중', label: '↻' }, + STARTING: { bg: 'bg-cyan-100 text-cyan-700', text: '시작중', label: '⏳' }, + STOPPED: { bg: 'bg-amber-100 text-amber-700', text: '중지됨', label: '⏸' }, + STOPPING: { bg: 'bg-orange-100 text-orange-700', text: '중지중', label: '⏸' }, + ABANDONED: { bg: 'bg-gray-100 text-gray-700', text: '포기됨', label: '—' }, + SCHEDULED: { bg: 'bg-violet-100 text-violet-700', text: '예정', label: '🕐' }, + UNKNOWN: { bg: 'bg-gray-100 text-gray-500', text: '알수없음', label: '?' }, +}; + +interface Props { + status: string; + className?: string; +} + +export default function StatusBadge({ status, className = '' }: Props) { + const config = STATUS_CONFIG[status] || STATUS_CONFIG.UNKNOWN; + return ( + + {config.label} + {config.text} + + ); +} + +// eslint-disable-next-line react-refresh/only-export-components +export function getStatusColor(status: string): string { + switch (status) { + case 'COMPLETED': return '#10b981'; + case 'FAILED': return '#ef4444'; + case 'STARTED': return '#3b82f6'; + case 'STARTING': return '#06b6d4'; + case 'STOPPED': return '#f59e0b'; + case 'STOPPING': return '#f97316'; + case 'SCHEDULED': return '#8b5cf6'; + default: return '#6b7280'; + } +} diff --git a/frontend/src/components/Toast.tsx b/frontend/src/components/Toast.tsx new file mode 100644 index 0000000..90e5750 --- /dev/null +++ b/frontend/src/components/Toast.tsx @@ -0,0 +1,37 @@ +import type { Toast as ToastType } from '../hooks/useToast'; + +const TYPE_STYLES: Record = { + success: 'bg-emerald-500', + error: 'bg-red-500', + warning: 'bg-amber-500', + info: 'bg-blue-500', +}; + +interface Props { + toasts: ToastType[]; + onRemove: (id: number) => void; +} + +export default function ToastContainer({ toasts, onRemove }: Props) { + if (toasts.length === 0) return null; + + return ( +
+ {toasts.map((toast) => ( +
+ {toast.message} + +
+ ))} +
+ ); +} diff --git a/frontend/src/components/bypass/BypassConfigModal.tsx b/frontend/src/components/bypass/BypassConfigModal.tsx new file mode 100644 index 0000000..6a3b854 --- /dev/null +++ b/frontend/src/components/bypass/BypassConfigModal.tsx @@ -0,0 +1,222 @@ +import { useState, useEffect } from 'react'; +import type { + BypassConfigRequest, + BypassConfigResponse, + BypassParamDto, + WebClientBeanInfo, +} from '../../api/bypassApi'; +import BypassStepBasic from './BypassStepBasic'; +import BypassStepParams from './BypassStepParams'; + +interface BypassConfigModalProps { + open: boolean; + editConfig: BypassConfigResponse | null; + webclientBeans: WebClientBeanInfo[]; + onSave: (data: BypassConfigRequest) => Promise; + onClose: () => void; +} + +type StepNumber = 1 | 2; + +const STEP_LABELS: Record = { + 1: '기본 정보', + 2: '파라미터', +}; + +const DEFAULT_FORM: Omit = { + domainName: '', + displayName: '', + webclientBean: '', + externalPath: '', + httpMethod: 'GET', + description: '', +}; + +export default function BypassConfigModal({ + open, + editConfig, + webclientBeans, + onSave, + onClose, +}: BypassConfigModalProps) { + const [step, setStep] = useState(1); + const [domainName, setDomainName] = useState(''); + const [displayName, setDisplayName] = useState(''); + const [webclientBean, setWebclientBean] = useState(''); + const [externalPath, setExternalPath] = useState(''); + const [httpMethod, setHttpMethod] = useState('GET'); + const [description, setDescription] = useState(''); + const [params, setParams] = useState([]); + const [saving, setSaving] = useState(false); + + useEffect(() => { + if (!open) return; + setStep(1); + if (editConfig) { + setDomainName(editConfig.domainName); + setDisplayName(editConfig.displayName); + setWebclientBean(editConfig.webclientBean); + setExternalPath(editConfig.externalPath); + setHttpMethod(editConfig.httpMethod); + setDescription(editConfig.description); + setParams(editConfig.params); + } else { + setDomainName(DEFAULT_FORM.domainName); + setDisplayName(DEFAULT_FORM.displayName); + setWebclientBean(DEFAULT_FORM.webclientBean); + setExternalPath(DEFAULT_FORM.externalPath); + setHttpMethod(DEFAULT_FORM.httpMethod); + setDescription(DEFAULT_FORM.description); + setParams([]); + } + }, [open, editConfig]); + + if (!open) return null; + + const handleBasicChange = (field: string, value: string) => { + switch (field) { + case 'domainName': setDomainName(value); break; + case 'displayName': setDisplayName(value); break; + case 'webclientBean': setWebclientBean(value); break; + case 'externalPath': setExternalPath(value); break; + case 'httpMethod': setHttpMethod(value); break; + case 'description': setDescription(value); break; + } + }; + + const handleSave = async () => { + setSaving(true); + try { + await onSave({ + domainName, + displayName, + webclientBean, + externalPath, + httpMethod, + description, + params, + }); + onClose(); + } finally { + setSaving(false); + } + }; + + const steps: StepNumber[] = [1, 2]; + + return ( +
+
e.stopPropagation()} + > + {/* 헤더 */} +
+

+ {editConfig ? 'Bypass API 수정' : 'Bypass API 등록'} +

+ + {/* 스텝 인디케이터 */} +
+ {steps.map((s, idx) => ( +
+
+ s + ? 'bg-wing-accent/30 text-wing-accent' + : 'bg-wing-card text-wing-muted border border-wing-border', + ].join(' ')} + > + {s} + + + {STEP_LABELS[s]} + +
+ {idx < steps.length - 1 && ( +
+ )} +
+ ))} +
+
+ + {/* 본문 */} +
+ {step === 1 && ( + + )} + {step === 2 && ( + + )} +
+ + {/* 하단 버튼 */} +
+
+ {step > 1 && ( + + )} +
+
+ {step === 1 && ( + + )} + {step < 2 ? ( + + ) : ( + + )} +
+
+
+
+ ); +} diff --git a/frontend/src/components/bypass/BypassStepBasic.tsx b/frontend/src/components/bypass/BypassStepBasic.tsx new file mode 100644 index 0000000..5056b07 --- /dev/null +++ b/frontend/src/components/bypass/BypassStepBasic.tsx @@ -0,0 +1,142 @@ +import type { WebClientBeanInfo } from '../../api/bypassApi'; + +interface BypassStepBasicProps { + domainName: string; + displayName: string; + webclientBean: string; + externalPath: string; + httpMethod: string; + description: string; + webclientBeans: WebClientBeanInfo[]; + isEdit: boolean; + onChange: (field: string, value: string) => void; +} + +export default function BypassStepBasic({ + domainName, + displayName, + webclientBean, + externalPath, + httpMethod, + description, + webclientBeans, + isEdit, + onChange, +}: BypassStepBasicProps) { + return ( +
+

+ BYPASS API의 기본 정보를 입력하세요. 도메인명을 기반으로 코드가 생성됩니다. +

+ +
+ {/* 도메인명 */} +
+ + onChange('domainName', e.target.value)} + disabled={isEdit} + placeholder="예: riskByImo" + pattern="[a-zA-Z][a-zA-Z0-9]*" + className={[ + 'w-full px-3 py-2 text-sm rounded-lg border', + 'border-wing-border bg-wing-card text-wing-text', + 'placeholder:text-wing-muted focus:outline-none focus:ring-2 focus:ring-wing-accent/50', + isEdit ? 'opacity-50 cursor-not-allowed' : '', + ].join(' ')} + /> +

영문 소문자/숫자 조합 (수정 불가)

+
+ + {/* 표시명 */} +
+ + onChange('displayName', e.target.value)} + placeholder="예: IMO 기반 리스크 조회" + className="w-full px-3 py-2 text-sm rounded-lg border border-wing-border bg-wing-card text-wing-text placeholder:text-wing-muted focus:outline-none focus:ring-2 focus:ring-wing-accent/50" + /> +
+ + {/* WebClient */} +
+ + +
+ + {/* 외부 API 경로 */} +
+ + onChange('externalPath', e.target.value)} + placeholder="/RiskAndCompliance/RisksByImos" + className="w-full px-3 py-2 text-sm rounded-lg border border-wing-border bg-wing-card text-wing-text placeholder:text-wing-muted focus:outline-none focus:ring-2 focus:ring-wing-accent/50" + /> +
+ + {/* HTTP 메서드 */} +
+ +
+ {['GET', 'POST'].map((method) => ( + + ))} +
+
+ + {/* 설명 */} +
+ +