shang-chunyu 3 weeks ago
commit
0702802317
100 changed files with 28522 additions and 0 deletions
  1. 47 0
      .gitignore
  2. 220 0
      CLAUDE.md
  3. 194 0
      LICENSE
  4. 207 0
      README.md
  5. 94 0
      docker-compose.yml
  6. 445 0
      docs/API.md
  7. 361 0
      docs/CHUNK_SIZE_LIMITS.md
  8. 165 0
      docs/CURRENT_IMPLEMENTATION.md
  9. 444 0
      docs/DEPLOYMENT.md
  10. 217 0
      docs/DESIGN.md
  11. 71 0
      docs/DEVELOPMENT_STANDARDS.md
  12. 219 0
      docs/EMBEDDING_MODEL_ID_FIX.md
  13. 29 0
      docs/FEATURE_SUMMARY.md
  14. 119 0
      docs/FEAT_ANALYSIS.md
  15. 94 0
      docs/INTERNAL_DEPLOYMENT_GUIDE.md
  16. 40 0
      docs/INTERNAL_DEPLOYMENT_SUMMARY.md
  17. 464 0
      docs/KNOWLEDGE_BASE_ENHANCEMENTS.md
  18. 139 0
      docs/LARGE_FILE_HANDLING.md
  19. 348 0
      docs/MEMORY_OPTIMIZATION_FIX.md
  20. 90 0
      docs/PDF_PREVIEW_FIX.md
  21. 225 0
      docs/PROJECT_EXPLANATION_JA.md
  22. BIN
      docs/PROJECT_EXPLANATION_JA.pdf
  23. 316 0
      docs/QUICK_START.md
  24. 87 0
      docs/RAG_COMPLETE_IMPLEMENTATION.md
  25. 249 0
      docs/SIMILARITY_SCORE_BUGFIX.md
  26. 158 0
      docs/SUPPORTED_FILE_TYPES.md
  27. 55 0
      docs/VECTOR_DB_COMPARISON_JA.md
  28. BIN
      docs/VECTOR_DB_COMPARISON_JA.pdf
  29. 265 0
      docs/VISION_PIPELINE_COMPLETE.md
  30. 68 0
      docs/design/feat-auto-title-generation.md
  31. 59 0
      docs/design/feat-cross-doc-comparison.md
  32. 37 0
      docs/design/feat-highlight-jump.md
  33. 52 0
      docs/design/feat-query-expansion-hyde.md
  34. 32 0
      docs/test_admin_features.md
  35. 41 0
      docs/testing/feat-auto-title-generation.md
  36. 43 0
      docs/testing/feat-query-expansion-hyde.md
  37. 0 0
      identifier.sqlite
  38. 57 0
      libreoffice-server/Dockerfile
  39. 214 0
      libreoffice-server/README.md
  40. 190 0
      libreoffice-server/main.py
  41. 498 0
      libreoffice-server/md_to_pdf.js
  42. 8 0
      libreoffice-server/package.json
  43. 5 0
      libreoffice-server/requirements.txt
  44. 68 0
      nginx/conf.d/kb.conf
  45. 22 0
      nginx/conf.d/ssl/cert.pem
  46. 28 0
      nginx/conf.d/ssl/key.pem
  47. 11 0
      nginx/generate-ssl.sh
  48. 32 0
      nginx/nginx.conf
  49. 16774 0
      package-lock.json
  50. 15 0
      package.json
  51. 12 0
      server/.dockerignore
  52. 44 0
      server/.env.sample
  53. 4 0
      server/.prettierrc
  54. 26 0
      server/Dockerfile
  55. 113 0
      server/README.md
  56. BIN
      server/chi_sim.traineddata
  57. BIN
      server/eng.traineddata
  58. 41 0
      server/eslint.config.mjs
  59. BIN
      server/jpn.traineddata
  60. 8 0
      server/nest-cli.json
  61. 104 0
      server/package.json
  62. 60 0
      server/pdf_to_images.py
  63. 8 0
      server/src/ai/ai.module.ts
  64. 32 0
      server/src/ai/embedding.service.ts
  65. 73 0
      server/src/api/api.controller.ts
  66. 19 0
      server/src/api/api.module.ts
  67. 45 0
      server/src/api/api.service.ts
  68. 22 0
      server/src/app.controller.spec.ts
  69. 12 0
      server/src/app.controller.ts
  70. 104 0
      server/src/app.module.ts
  71. 8 0
      server/src/app.service.ts
  72. 15 0
      server/src/auth/admin.guard.ts
  73. 23 0
      server/src/auth/auth.controller.ts
  74. 27 0
      server/src/auth/auth.module.ts
  75. 28 0
      server/src/auth/auth.service.ts
  76. 22 0
      server/src/auth/jwt-auth.guard.ts
  77. 33 0
      server/src/auth/jwt.strategy.ts
  78. 5 0
      server/src/auth/local-auth.guard.ts
  79. 21 0
      server/src/auth/local.strategy.ts
  80. 4 0
      server/src/auth/public.decorator.ts
  81. 223 0
      server/src/chat/chat.controller.ts
  82. 26 0
      server/src/chat/chat.module.ts
  83. 532 0
      server/src/chat/chat.service.ts
  84. 739 0
      server/src/chat/chat.service.updated.ts
  85. 22 0
      server/src/common/constants.ts
  86. 39 0
      server/src/defaults.ts
  87. 10 0
      server/src/elasticsearch/elasticsearch.module.ts
  88. 578 0
      server/src/elasticsearch/elasticsearch.service.ts
  89. 9 0
      server/src/i18n/i18n.module.ts
  90. 227 0
      server/src/i18n/i18n.service.ts
  91. 264 0
      server/src/i18n/messages.ts
  92. 31 0
      server/src/import-task/import-task.controller.ts
  93. 46 0
      server/src/import-task/import-task.entity.ts
  94. 18 0
      server/src/import-task/import-task.module.ts
  95. 232 0
      server/src/import-task/import-task.service.ts
  96. 351 0
      server/src/knowledge-base/chunk-config.service.ts
  97. 11 0
      server/src/knowledge-base/dto/create-knowledge-base.dto.ts
  98. 242 0
      server/src/knowledge-base/embedding.service.ts
  99. 304 0
      server/src/knowledge-base/knowledge-base.controller.ts
  100. 89 0
      server/src/knowledge-base/knowledge-base.entity.ts

+ 47 - 0
.gitignore

@@ -0,0 +1,47 @@
+# Dependencies
+.claude
+/node_modules
+/web/node_modules
+/server/node_modules
+
+# Build outputs
+/server/dist
+/web/dist
+/web/build
+/server/build
+
+# Data and uploads
+/data
+/uploads
+/temp
+/server/uploads
+/server/data
+/server/temp
+
+# Env files
+.env.local
+.env.development
+.env.test
+/web/.env.local
+
+# Logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Coverage
+coverage
+.nyc_output
+
+# IDE
+.vscode
+.idea
+*.swp
+*.swo
+/server/.env
+/libreoffice-server/.venv
+/libreoffice-server/uploads
+
+# temp
+analyze_translations.py

+ 220 - 0
CLAUDE.md

@@ -0,0 +1,220 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+Lumina is a full-stack RAG (Retrieval-Augmented Generation) Q&A system built with React 19 + NestJS. It's a monorepo with Japanese/Chinese documentation but English code.
+
+**Key Features:**
+- Multi-model support (OpenAI-compatible APIs + Google Gemini native SDK)
+- Dual processing modes: Fast (Tika text-only) and High-precision (Vision pipeline)
+- User isolation with JWT authentication and per-user knowledge bases
+- Hybrid search (vector + keyword) with Elasticsearch
+- Multi-language interface (Japanese, Chinese, English)
+- Streaming responses via Server-Sent Events (SSE)
+
+## Development Setup
+
+### Prerequisites
+- Node.js 18+
+- Yarn
+- Docker & Docker Compose
+
+### Initial Setup
+```bash
+# Install dependencies
+yarn install
+
+# Start infrastructure services
+docker-compose up -d elasticsearch tika libreoffice
+
+# Configure environment
+cp server/.env.sample server/.env
+# Edit server/.env with API keys and configuration
+```
+
+### Development Commands
+```bash
+# Start both frontend and backend in development mode
+yarn dev
+
+# Frontend only (port 13001)
+cd web && yarn dev
+
+# Backend only (port 3001)
+cd server && yarn start:dev
+
+# Run tests
+cd server && yarn test
+cd server && yarn test:e2e
+
+# Lint and format
+cd server && yarn lint
+cd server && yarn format
+```
+
+### Docker Services
+- **Elasticsearch**: 9200 (vector storage)
+- **Apache Tika**: 9998 (document text extraction)
+- **LibreOffice Server**: 8100 (document conversion)
+- **Backend API**: 3001
+- **Frontend**: 13001 (dev), 80/443 (production via nginx)
+
+## Architecture
+
+### Project Structure
+```
+lumina/
+├── web/                    # React frontend (Vite)
+│   ├── components/         # UI components (ChatInterface, ConfigPanel, etc.)
+│   ├── contexts/          # React Context providers
+│   ├── services/          # API client services
+│   └── utils/             # Utility functions
+├── server/                # NestJS backend
+│   ├── src/
+│   │   ├── ai/            # AI services (embedding, etc.)
+│   │   ├── api/           # API module
+│   │   ├── auth/          # JWT authentication
+│   │   ├── chat/          # Chat/RAG module
+│   │   ├── elasticsearch/ # Elasticsearch integration
+│   │   ├── import-task/   # Import task management
+│   │   ├── knowledge-base/# Knowledge base management
+│   │   ├── libreoffice/   # LibreOffice integration
+│   │   ├── model-config/  # Model configuration management
+│   │   ├── vision/        # Vision model integration
+│   │   └── vision-pipeline/# Vision pipeline orchestration
+│   ├── data/              # SQLite database storage
+│   ├── uploads/           # Uploaded files storage
+│   └── temp/              # Temporary files
+├── docs/                  # Comprehensive documentation (Japanese/Chinese)
+├── nginx/                 # Nginx configuration
+├── libreoffice-server/    # LibreOffice conversion service (Python/FastAPI)
+└── docker-compose.yml     # Docker orchestration
+```
+
+### Key Architectural Concepts
+
+**Dual Processing Modes:**
+1. **Fast Mode**: Apache Tika for text-only extraction (quick, no API cost)
+2. **High-Precision Mode**: Vision Pipeline (LibreOffice → PDF → Images → Vision Model) for mixed image/text documents (slower, incurs API costs)
+
+**Multi-Model Support:**
+- OpenAI-compatible APIs (OpenAI, DeepSeek, Claude, etc.)
+- Google Gemini native SDK
+- Configurable LLM, Embedding, and Rerank models
+
+**RAG System:**
+- Hybrid search (vector + keyword) with Elasticsearch
+- Streaming responses via Server-Sent Events (SSE)
+- Source citation and similarity scoring
+- Chunk configuration (size, overlap)
+
+## Development Process (Documentation-Driven)
+
+Every feature development MUST follow this sequence:
+
+1. **Design Phase**: Create a design document in `docs/design/feat-[feature-name].md` before writing any code. Outline the requirements, architecture, and UI/UX changes.
+2. **Implementation Phase**: Develop the feature following the design and code standards.
+3. **Verification Phase**: Create a test case document in `docs/testing/feat-[feature-name].md` documenting test results and edge cases.
+
+For complex features, refer to `docs/FEAT_ANALYSIS.md` for long-term roadmap and architectural guidance.
+
+## Code Standards
+
+### Naming Conventions
+- **Project Name**: Lumina
+- **Monorepo Packages**: Use `lumina-` prefix for all sub-packages (e.g., `lumina-web`, `lumina-server`).
+
+### Language Requirements
+- **Code comments must be in Japanese**
+- **Log messages must be in Japanese**
+- **User Interface strings must follow i18n standards** (JA, ZH, EN).
+- **API error/response messages must support i18n** via translation keys.
+
+### Testing
+- Backend uses Jest for unit and e2e tests
+- Frontend currently has no test framework configured
+- Run tests: `cd server && yarn test` or `yarn test:e2e`
+
+### Code Quality
+- ESLint and Prettier configured for backend
+- Format code: `cd server && yarn format`
+- Lint code: `cd server && yarn lint`
+
+## Common Development Tasks
+
+### Adding a New API Endpoint
+1. Create design doc in `docs/design/`
+2. Create controller in appropriate module under `server/src/`
+3. Add service methods with Japanese comments
+4. Update DTOs and validation
+5. Add tests in `*.spec.ts` files
+6. Create test case doc in `docs/testing/`
+
+### Adding a New Frontend Component
+1. Create design doc in `docs/design/`
+2. Create component in `web/components/`
+3. Add TypeScript interfaces in `web/types.ts`
+4. Use Tailwind CSS for styling
+5. Connect to backend services in `web/services/`
+6. Create test case doc in `docs/testing/`
+
+### Debugging
+- Backend logs are in Chinese
+- Check Elasticsearch: `curl http://localhost:9200/_cat/indices`
+- Check Tika: `curl http://localhost:9998/tika`
+- Check LibreOffice: `curl http://localhost:8100/health`
+
+## Environment Configuration
+
+Key environment variables (`server/.env`):
+- `OPENAI_API_KEY`: OpenAI-compatible API key
+- `GEMINI_API_KEY`: Google Gemini API key
+- `ELASTICSEARCH_HOST`: Elasticsearch URL (default: http://localhost:9200)
+- `TIKA_HOST`: Apache Tika URL (default: http://localhost:9998)
+- `LIBREOFFICE_URL`: LibreOffice server URL (default: http://localhost:8100)
+- `JWT_SECRET`: JWT signing secret
+
+## Deployment
+
+### Development
+```bash
+docker-compose up -d elasticsearch tika libreoffice
+yarn dev
+```
+
+### Production
+```bash
+docker-compose up -d  # Builds and starts all services
+```
+
+### Ports in Production
+- Frontend: 80/443 (via nginx)
+- Backend API: 3001 (proxied through nginx)
+- Elasticsearch: 9200
+- Tika: 9998
+- LibreOffice: 8100
+
+## Troubleshooting
+
+### Common Issues
+1. **Elasticsearch not starting**: Check memory limits in docker-compose.yml
+2. **File upload failures**: Ensure `uploads/` and `temp/` directories exist with proper permissions
+3. **Vision pipeline errors**: Verify LibreOffice server is running and accessible
+4. **API key errors**: Check environment variables in `server/.env`
+
+### Database Management
+- SQLite database: `server/data/metadata.db`
+- Elasticsearch indices: Managed automatically by the application
+- To reset: Delete `server/data/metadata.db` and Elasticsearch data volume
+
+## Documentation
+
+- **README.md**: Project overview in Japanese
+- **docs/**: Comprehensive documentation (mostly Japanese/Chinese)
+- **DESIGN.md**: System architecture and design
+- **API.md**: API reference
+- **DEVELOPMENT_STANDARDS.md**: Mandates Japanese comments/logs
+
+When modifying code, always add Japanese comments as required by development standards. The project has extensive existing documentation in Japanese/Chinese - refer to `docs/` directory for detailed technical information.

+ 194 - 0
LICENSE

@@ -0,0 +1,194 @@
+木兰宽松许可证,第2版
+
+木兰宽松许可证,第2版
+
+2020年1月 http://license.coscl.org.cn/MulanPSL2
+
+您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束:
+
+0.   定义
+
+“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
+
+“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
+
+“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
+
+“法人实体” 是指提交贡献的机构及其“关联实体”。
+
+“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是
+指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
+
+1.   授予版权许可
+
+每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可
+以复制、使用、修改、分发其“贡献”,不论修改与否。
+
+2.   授予专利许可
+
+每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定
+撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡
+献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软
+件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“
+关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或
+其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权
+行动之日终止。
+
+3.   无商标许可
+
+“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定
+的声明义务而必须使用除外。
+
+4.   分发限制
+
+您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“
+本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
+
+5.   免责声明与责任限制
+
+“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对
+任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于
+何种法律理论,即使其曾被建议有此种损失的可能性。
+
+6.   语言
+
+“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文
+版为准。
+
+条款结束
+
+如何将木兰宽松许可证,第2版,应用到您的软件
+
+如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
+
+1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
+
+2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中;
+
+3, 请将如下声明文本放入每个源文件的头部注释中。
+
+Copyright (c) [Year] [name of copyright holder]
+[Software Name] is licensed under Mulan PSL v2.
+You can use this software according to the terms and conditions of the Mulan
+PSL v2.
+You may obtain a copy of Mulan PSL v2 at:
+         http://license.coscl.org.cn/MulanPSL2
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+See the Mulan PSL v2 for more details.
+
+Mulan Permissive Software License,Version 2
+
+Mulan Permissive Software License,Version 2 (Mulan PSL v2)
+
+January 2020 http://license.coscl.org.cn/MulanPSL2
+
+Your reproduction, use, modification and distribution of the Software shall
+be subject to Mulan PSL v2 (this License) with the following terms and
+conditions:
+
+0. Definition
+
+Software means the program and related documents which are licensed under
+this License and comprise all Contribution(s).
+
+Contribution means the copyrightable work licensed by a particular
+Contributor under this License.
+
+Contributor means the Individual or Legal Entity who licenses its
+copyrightable work under this License.
+
+Legal Entity means the entity making a Contribution and all its
+Affiliates.
+
+Affiliates means entities that control, are controlled by, or are under
+common control with the acting entity under this License, ‘control’ means
+direct or indirect ownership of at least fifty percent (50%) of the voting
+power, capital or other securities of controlled or commonly controlled
+entity.
+
+1. Grant of Copyright License
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to you a perpetual, worldwide, royalty-free, non-exclusive,
+irrevocable copyright license to reproduce, use, modify, or distribute its
+Contribution, with modification or not.
+
+2. Grant of Patent License
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to you a perpetual, worldwide, royalty-free, non-exclusive,
+irrevocable (except for revocation under this Section) patent license to
+make, have made, use, offer for sale, sell, import or otherwise transfer its
+Contribution, where such patent license is only limited to the patent claims
+owned or controlled by such Contributor now or in future which will be
+necessarily infringed by its Contribution alone, or by combination of the
+Contribution with the Software to which the Contribution was contributed.
+The patent license shall not apply to any modification of the Contribution,
+and any other combination which includes the Contribution. If you or your
+Affiliates directly or indirectly institute patent litigation (including a
+cross claim or counterclaim in a litigation) or other patent enforcement
+activities against any individual or entity by alleging that the Software or
+any Contribution in it infringes patents, then any patent license granted to
+you under this License for the Software shall terminate as of the date such
+litigation or activity is filed or taken.
+
+3. No Trademark License
+
+No trademark license is granted to use the trade names, trademarks, service
+marks, or product names of Contributor, except as required to fulfill notice
+requirements in section 4.
+
+4. Distribution Restriction
+
+You may distribute the Software in any medium with or without modification,
+whether in source or executable forms, provided that you provide recipients
+with a copy of this License and retain copyright, patent, trademark and
+disclaimer statements in the Software.
+
+5. Disclaimer of Warranty and Limitation of Liability
+
+THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY
+KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR
+COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT
+LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING
+FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO
+MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGES.
+
+6. Language
+
+THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION
+AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF
+DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION
+SHALL PREVAIL.
+
+END OF THE TERMS AND CONDITIONS
+
+How to Apply the Mulan Permissive Software License,Version 2
+(Mulan PSL v2) to Your Software
+
+To apply the Mulan PSL v2 to your work, for easy identification by
+recipients, you are suggested to complete following three steps:
+
+i. Fill in the blanks in following statement, including insert your software
+name, the year of the first publication of your software, and your name
+identified as the copyright owner;
+
+ii. Create a file named "LICENSE" which contains the whole context of this
+License in the first directory of your software package;
+
+iii. Attach the statement to the appropriate annotated syntax at the
+beginning of each source file.
+
+Copyright (c) [Year] [name of copyright holder]
+[Software Name] is licensed under Mulan PSL v2.
+You can use this software according to the terms and conditions of the Mulan
+PSL v2.
+You may obtain a copy of Mulan PSL v2 at:
+         http://license.coscl.org.cn/MulanPSL2
+THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
+KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
+See the Mulan PSL v2 for more details.

+ 207 - 0
README.md

@@ -0,0 +1,207 @@
+# Lumina (簡易ナレッジベース)
+
+React + NestJS をベースにしたフルスタックのナレッジベースQ&Aシステムです。マルチモデル、多言語対応の RAG (検索拡張生成) 機能を備えています。
+
+## ✨ 特徴
+
+- 🔐 **ユーザーシステム**: ユーザー登録、ログイン、権限管理を完備
+- 🤖 **マルチモデル対応**: OpenAI 互換インターフェース + Google Gemini ネイティブサポート
+- 📚 **インテリジェント・ナレッジベース**: ドキュメントのアップロード、チャンク分割、ベクトル化、ハイブリッド検索
+- 💬 **ストリーミング会話**: 処理状況と生成内容をリアルタイムに表示
+- 🔍 **引用追跡**: 回答の根拠となるドキュメントのソースと関連セグメントを明確に表示
+- 🌍 **多言語対応**: 日本語、中国語、英語のインターフェースおよびAI回答に対応
+- 👁️ **ビジョン機能**: 画像処理をサポートするマルチモーダルモデルに対応
+- ⚙️ **柔軟な設定**: ユーザー独自のAPIキーと推論パラメータのカスタマイズが可能
+- 🎯 **デュアルモード処理**: 高速モード (Tika) + 高精度モード (Vision Pipeline)
+- 💰 **コスト管理**: ユーザーの利用枠(クォータ)管理とコスト見積もり
+
+## 🏗️ 技術スタック
+
+### フロントエンド
+
+- **フレームワーク**: React 19 + TypeScript + Vite
+- **スタイリング**: Tailwind CSS
+- **アイコン**: Lucide React
+- **状態管理**: React Context
+
+### バックエンド
+
+- **フレームワーク**: NestJS + TypeScript
+- **AI フレームワーク**: LangChain
+- **データベース**: SQLite (メタデータ) + Elasticsearch (ベクトルストレージ)
+- **ファイル処理**: Apache Tika + Vision Pipeline
+- **認証**: JWT
+- **ドキュメント変換**: LibreOffice + ImageMagick
+
+## 🏢 内网部署
+
+本系统支持在内网环境中部署。主要修改包括:
+
+- **外部资源**: 已将 KaTeX CSS 从外部 CDN 移至本地资源
+- **AI模型**: 支持配置内网AI模型服务,无需访问外部API
+- **构建配置**: Dockerfiles 可配置使用内网镜像源
+
+有关详细配置说明,请参阅 [内网部署指南](INTERNAL_DEPLOYMENT_GUIDE.md)。
+
+## 🚀 クイックスタート
+
+### 前提条件
+
+- Node.js 18+
+- Yarn
+- Docker & Docker Compose
+
+### 1. プロジェクトのクローン
+
+```bash
+git clone <repository-url>
+cd lumina
+```
+
+### 2. 依存関係のインストール
+
+```bash
+yarn install
+```
+
+### 3. 基本サービスの起動
+
+```bash
+docker-compose up -d elasticsearch tika libreoffice
+```
+
+### 4. 環境変数の設定
+
+```bash
+# バックエンドの環境設定
+cp server/.env.sample server/.env
+# server/.env ファイルを編集(APIキーなどを設定)
+
+# フロントエンドの環境設定
+cp web/.env.example web/.env
+# web/.env ファイルを編集(必要に応じてフロントエンド設定を変更)
+```
+
+設定の詳細については、`server/.env.sample` と `web/.env.example` ファイル内のコメントを参照してください。
+
+### 5. 開発サーバーの起動
+
+```bash
+yarn dev
+```
+
+<http://localhost:5173> にアクセスして開始してください!
+
+## 📖 利用ガイド
+
+### 1. ユーザー登録/ログイン
+
+- 初回利用時はアカウント登録が必要です。
+- 各ユーザーは独立したナレッジベースとモデル設定を持ちます。
+
+### 2. AIモデルの設定
+
+- 「モデル管理」からAIモデルを追加します。
+- OpenAI、DeepSeek、Claude などの互換インターフェースをサポートしています。
+- Google Gemini ネイティブインターフェースをサポートしています。
+- LLM、Embedding、Rerank モデルを設定可能です。
+
+### 3. ドキュメントのアップロード
+
+- PDF, Word, PPT, Excel など、多様なフォーマットに対応しています。
+- 高速モード (テキストのみ) または高精度モード (画像・テキスト混合) を選択可能です。
+- ドキュメントのチャンクサイズとオーバーラップを調整可能です。
+- ベクトル化に使用する Embedding モデルを選択します。
+
+### 4. インテリジェントQ&Aの開始
+
+- アップロードしたドキュメントに基づいて質問応答を行います。
+- 検索と生成のプロセスをリアルタイムで確認できます。
+- 回答の出典や関連ドキュメントの断片を確認できます。
+
+## 🔧 設定の説明
+
+### モデル設定
+
+- **LLM モデル**: 対話生成に使用 (例: GPT-4, Gemini-1.5-Pro)
+- **Embedding モデル**: ドキュメントのベクトル化に使用 (例: text-embedding-3-small)
+- **Rerank モデル**: 検索結果の再ランキングに使用 (オプション)
+
+### 推論パラメータ
+
+- **Temperature**: 回答のランダム性を制御 (0-1)
+- **Max Tokens**: 最大出力長
+- **Top K**: 検索するドキュメントセグメントの数
+- **類似度しきい値**: 低い関連性のコンテンツをフィルタリング
+
+## 📁 プロジェクト構造
+
+```
+lumina/
+├── web/                 # フロントエンド・アプリケーション
+│   ├── components/      # React コンポーネント
+│   ├── services/        # API サービス
+│   ├── contexts/        # React Context
+│   └── utils/          # ユーティリティ関数
+├── server/             # バックエンド・アプリケーション
+│   ├── src/
+│   │   ├── auth/       # 認証モジュール
+│   │   ├── chat/       # チャットモジュール
+│   │   ├── knowledge-base/ # ナレッジベースモジュール
+│   │   ├── model-config/   # モデル設定モジュール
+│   │   └── user/       # ユーザーモジュール
+│   └── data/           # データストレージ
+├── docs/               # プロジェクトドキュメント
+└── docker-compose.yml  # Docker 設定
+```
+
+## 📚 ドキュメント
+
+- [システム設計ドキュメント](docs/DESIGN.md)
+- [現在の実装状況](docs/CURRENT_IMPLEMENTATION.md)
+- [API ドキュメント](docs/API.md)
+- [デプロイガイド](docs/DEPLOYMENT.md)
+- [RAG 機能の実装](docs/rag_complete_implementation.md)
+
+## 🐳 Docker デプロイ
+
+### 開発環境
+
+```bash
+# 基本サービスの起動
+docker-compose up -d elasticsearch tika
+
+# ローカル開発
+yarn dev
+```
+
+### 本番環境
+
+```bash
+# すべてのサービスをビルドして起動
+docker-compose up -d
+```
+
+## 🤝 貢献ガイド
+
+1. プロジェクトを Fork する
+2. 機能ブランチを作成する (`git checkout -b feature/AmazingFeature`)
+3. 変更をコミットする (`git commit -m 'Add some AmazingFeature'`)
+4. ブランチにプッシュする (`git push origin feature/AmazingFeature`)
+5. Pull Request を開く
+
+## 📄 ライセンス
+
+このプロジェクトは MIT ライセンスの下で提供されています。詳細は [LICENSE](LICENSE) ファイルをご覧ください。
+
+## 🙏 謝辞
+
+- [LangChain](https://langchain.com/) - AI アプリケーション開発フレームワーク
+- [NestJS](https://nestjs.com/) - Node.js バックエンドフレームワーク
+- [React](https://react.dev/) - フロントエンド UI フレームワーク
+- [Elasticsearch](https://www.elastic.co/) - 検索および分析エンジン
+- [Apache Tika](https://tika.apache.org/) - ドキュメント解析ツール
+
+## 📞 サポート
+
+質問や提案がある場合は、[Issue](../../issues) を送信するか、メンテナーに連絡してください。

+ 94 - 0
docker-compose.yml

@@ -0,0 +1,94 @@
+services:
+  es:
+    image: elasticsearch:9.2.1
+    container_name: lumina-es
+    environment:
+      - discovery.type=single-node
+      - xpack.security.enabled=false
+      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
+    ports:
+      - "9200:9200"
+    volumes:
+      - lumina-es-data:/usr/share/elasticsearch/data
+    networks:
+      - lumina-network
+  #    restart: unless-stopped
+
+  tika:
+    image: apache/tika:latest
+    container_name: lumina-tika
+    ports:
+      - "9998:9998"
+    networks:
+      - lumina-network
+    restart: unless-stopped
+
+  libreoffice:
+    build:
+      context: ./libreoffice-server
+      dockerfile: Dockerfile
+    container_name: lumina-libreoffice
+    ports:
+      - "8100:8100"
+    volumes:
+      - ./uploads:/app/uploads
+      - ./temp:/temp
+    networks:
+      - lumina-network
+    restart: unless-stopped
+  # server:
+  #   build:
+  #     context: ./server
+  #     dockerfile: Dockerfile
+  #   container_name: lumina-server
+  #   environment:
+  #     - NODE_ENV=production
+  #     - NODE_OPTIONS=--max-old-space-size=8192
+  #     - PORT=3001
+  #     - DATABASE_PATH=/app/data/metadata.db
+  #     - ELASTICSEARCH_HOST=http://es:9200
+  #     - TIKA_HOST=http://tika:9998
+  #     - LIBREOFFICE_URL=http://libreoffice:8100
+  #     - JWT_SECRET=13405a7d-742a-41f5-8b34-012735acffea
+  #     - UPLOAD_FILE_PATH=/app/uploads
+  #     - DEFAULT_VECTOR_DIMENSIONS=2048
+  #     - TEMP_DIR=/app/temp
+  #     - CHUNK_BATCH_SIZE=50
+  #   volumes:
+  #     - ./data:/app/data
+  #     - ./uploads:/app/uploads
+  #     - ./temp:/app/temp
+  #   depends_on:
+  #     - es
+  #     - tika
+  #     - libreoffice
+  #   #    restart: unless-stopped
+  #   networks:
+  #     - lumina-network
+
+  # web:
+  #   build:
+  #     context: .
+  #     dockerfile: ./web/Dockerfile
+  #     args:
+  #       - VITE_API_BASE_URL=/api
+  #   container_name: lumina-web
+  #   depends_on:
+  #     - server
+  #   ports:
+  #     - "80:80"
+  #     - "443:443"
+  #   volumes:
+  #     - ./nginx/conf.d:/etc/nginx/conf.d
+  #   networks:
+  #     - lumina-network
+
+networks:
+  lumina-network:
+    driver: bridge
+
+volumes:
+  lumina-es-data:
+    driver: local
+  lumina-data:
+    driver: local

+ 445 - 0
docs/API.md

@@ -0,0 +1,445 @@
+# API リファレンス
+
+## 基本情報
+
+- **ベース URL**: `http://localhost:3000`
+- **認証方式**: JWT Bearer トークン
+- **Content-Type**: `application/json`
+
+## 認証 API
+
+### ユーザー登録
+
+```http
+POST /auth/register
+Content-Type: application/json
+
+{
+  "username": "string",
+  "password": "string"
+}
+```
+
+**レスポンス**:
+
+```json
+{
+  "message": "ユーザーが正常に作成されました",
+  "user": {
+    "id": "string",
+    "username": "string",
+    "isAdmin": false
+  }
+}
+```
+
+### ユーザーログイン
+
+```http
+POST /auth/login
+Content-Type: application/json
+
+{
+  "username": "string", 
+  "password": "string"
+}
+```
+
+**レスポンス**:
+
+```json
+{
+  "access_token": "jwt_token_string",
+  "user": {
+    "id": "string",
+    "username": "string",
+    "isAdmin": false
+  }
+}
+```
+
+### パスワード変更
+
+```http
+POST /auth/change-password
+Authorization: Bearer <token>
+Content-Type: application/json
+
+{
+  "currentPassword": "string",
+  "newPassword": "string"
+}
+```
+
+## モデル設定 API
+
+### モデル一覧の取得
+
+```http
+GET /model-configs
+Authorization: Bearer <token>
+```
+
+**レスポンス**:
+
+```json
+[
+  {
+    "id": "string",
+    "name": "string",
+    "provider": "openai|gemini",
+    "modelId": "string",
+    "baseUrl": "string",
+    "type": "llm|embedding|rerank",
+    "supportsVision": boolean
+  }
+]
+```
+
+### モデル設定の作成
+
+```http
+POST /model-configs
+Authorization: Bearer <token>
+Content-Type: application/json
+
+{
+  "name": "string",
+  "provider": "openai|gemini", 
+  "modelId": "string",
+  "baseUrl": "string",
+  "apiKey": "string",
+  "type": "llm|embedding|rerank",
+  "supportsVision": boolean
+}
+```
+
+### モデル設定の更新
+
+```http
+PUT /model-configs/:id
+Authorization: Bearer <token>
+Content-Type: application/json
+
+{
+  "name": "string",
+  "apiKey": "string",
+  // ... その他のフィールド
+}
+```
+
+### モデル設定の削除
+
+```http
+DELETE /model-configs/:id
+Authorization: Bearer <token>
+```
+
+## ナレッジベース API
+
+### ファイルのアップロード
+
+```http
+POST /upload
+Authorization: Bearer <token>
+Content-Type: multipart/form-data
+
+{
+  "file": File,
+  "chunkSize": number,
+  "chunkOverlap": number,
+  "embeddingModelId": "string",
+  "mode": "fast|precise"  // 処理モード
+}
+```
+
+**レスポンス**:
+
+```json
+{
+  "id": "string",
+  "name": "string",
+  "originalName": "string",
+  "size": number,
+  "mimetype": "string",
+  "status": "pending|indexing|completed|failed"
+}
+```
+
+### ファイル一覧の取得
+
+```http
+GET /knowledge-bases
+Authorization: Bearer <token>
+```
+
+**レスポンス**:
+
+```json
+[
+  {
+    "id": "string",
+    "name": "string", 
+    "originalName": "string",
+    "size": number,
+    "mimetype": "string",
+    "status": "pending|indexing|completed|failed",
+    "createdAt": "datetime"
+  }
+]
+```
+
+### ファイルの削除
+
+```http
+DELETE /knowledge-bases/:id
+Authorization: Bearer <token>
+```
+
+### ナレッジベースの全消去
+
+```http
+DELETE /knowledge-bases/clear
+Authorization: Bearer <token>
+```
+
+## チャット API
+
+### ストリーミングチャット
+
+```http
+POST /chat/stream
+Authorization: Bearer <token>
+Content-Type: application/json
+
+{
+  "message": "string",
+  "history": [
+    {
+      "role": "user|assistant",
+      "content": "string"
+    }
+  ],
+  "userLanguage": "zh|en|ja"
+}
+```
+
+**レスポンス**: Server-Sent Events (SSE)
+
+```
+data: {"type": "content", "data": "ナレッジベースを検索中..."}
+
+data: {"type": "content", "data": "関連情報が見つかりました..."}
+
+data: {"type": "content", "data": "回答内容の断片"}
+
+data: {"type": "sources", "data": [
+  {
+    "fileName": "string",
+    "content": "string", 
+    "score": number,
+    "chunkIndex": number
+  }
+]}
+
+data: [DONE]
+```
+
+## ユーザー設定 API
+
+### ユーザー設定の取得
+
+```http
+GET /user-settings
+Authorization: Bearer <token>
+```
+
+**レスポンス**:
+
+```json
+{
+  "selectedLLMId": "string",
+  "selectedEmbeddingId": "string", 
+  "selectedRerankId": "string",
+  "temperature": number,
+  "maxTokens": number,
+  "topK": number,
+  "enableRerank": boolean,
+  "scoreThreshold": number,
+  "similarityThreshold": number,
+  "enableFullTextSearch": boolean,
+  "language": "zh|en|ja"
+}
+```
+
+### ユーザー設定の更新
+
+```http
+PUT /user-settings
+Authorization: Bearer <token>
+Content-Type: application/json
+
+{
+  "selectedLLMId": "string",
+  "temperature": number,
+  "maxTokens": number,
+  // ... その他の設定フィールド
+}
+```
+
+## Vision Pipeline API
+
+### 推奨モードの取得
+
+```http
+GET /api/vision/recommend-mode?file=xxx&size=xxx
+Authorization: Bearer <token>
+```
+
+**レスポンス**:
+
+```json
+{
+  "recommendedMode": "precise",
+  "reason": "ファイルサイズが大きいため、高精度モードを推奨します",
+  "estimatedCost": 0.5,
+  "estimatedTime": 60,
+  "warnings": ["処理時間が長くなる可能性があります", "API 利用料が発生します"]
+}
+```
+
+### LibreOffice 変換サービス
+
+```http
+POST /libreoffice/convert
+Content-Type: multipart/form-data
+
+{
+  "file": File
+}
+```
+
+**レスポンス**:
+
+```json
+{
+  "pdf_path": "/uploads/document.pdf",
+  "converted": true,
+  "original": "document.docx",
+  "file_size": 102400
+}
+```
+
+### ヘルスチェック
+
+```http
+GET /libreoffice/health
+```
+
+**レスポンス**:
+
+```json
+{
+  "status": "healthy",
+  "service": "libreoffice-converter",
+  "version": "1.0.0",
+  "uptime": 3600.5
+}
+```
+
+## ユーザー管理 API (管理者用)
+
+### ユーザー一覧の取得
+
+```http
+GET /users
+Authorization: Bearer <admin_token>
+```
+
+### ユーザーの作成
+
+```http
+POST /users
+Authorization: Bearer <admin_token>
+Content-Type: application/json
+
+{
+  "username": "string",
+  "password": "string",
+  "isAdmin": boolean
+}
+```
+
+### ユーザーの削除
+
+```http
+DELETE /users/:id
+Authorization: Bearer <admin_token>
+```
+
+## エラーレスポンス形式
+
+```json
+{
+  "statusCode": number,
+  "message": "string",
+  "error": "string"
+}
+```
+
+## ステータスコードの説明
+
+- `200` - 成功
+- `201` - 作成成功
+- `400` - リクエストパラメータの不正
+- `401` - 認証エラー / トークン無効
+- `403` - 権限不足
+- `404` - リソースが見つかりません
+- `409` - リソースの競合
+- `500` - サーバー内部エラー
+
+## 実装例
+
+### JavaScript/TypeScript
+
+```javascript
+// ログイン
+const loginResponse = await fetch('/auth/login', {
+  method: 'POST',
+  headers: {
+    'Content-Type': 'application/json'
+  },
+  body: JSON.stringify({
+    username: 'user',
+    password: 'password'
+  })
+});
+
+const { access_token } = await loginResponse.json();
+
+// ファイル一覧の取得
+const filesResponse = await fetch('/knowledge-bases', {
+  headers: {
+    'Authorization': `Bearer ${access_token}`
+  }
+});
+
+const files = await filesResponse.json();
+
+// ストリーミングチャット
+const chatResponse = await fetch('/chat/stream', {
+  method: 'POST',
+  headers: {
+    'Authorization': `Bearer ${access_token}`,
+    'Content-Type': 'application/json'
+  },
+  body: JSON.stringify({
+    message: 'こんにちは',
+    history: [],
+    userLanguage: 'ja'
+  })
+});
+
+const reader = chatResponse.body.getReader();
+// SSE ストリームの処理...
+```

+ 361 - 0
docs/CHUNK_SIZE_LIMITS.md

@@ -0,0 +1,361 @@
+# チャンクサイズの制限に関する完全スキーム
+
+## 🎯 設計目標
+
+**主要な問題の解決:**
+
+1. ✅ チャンクサイズがモデルの入力制限を超えないようにする
+2. ✅ 環境変数でグローバルな上限を設定可能にする
+3. ✅ フロントエンドのスライダーで動的に制限し、上限を超えられないようにする
+4. ✅ バックエンドで自動検証と調整を行う
+
+---
+
+## 📋 設定階層構造
+
+```
+┌─────────────────────────────────────────┐
+│   環境変数の設定 (server/.env)          │
+│   MAX_CHUNK_SIZE=8191                   │
+│   MAX_OVERLAP_SIZE=200                  │
+└─────────────────────────────────────────┘
+            ↓ 優先度1(最も厳格)
+┌─────────────────────────────────────────┐
+│   モデル制限設定 (ChunkConfigService)   │
+│   OpenAI: 8191 tokens                   │
+│   Gemini: 2048 tokens                   │
+└─────────────────────────────────────────┘
+            ↓ 優先度2
+┌─────────────────────────────────────────┐
+│   ユーザー設定 (フロントエンドスライダー)│
+│   chunkSize: 200 tokens                 │
+│   chunkOverlap: 40 tokens               │
+└─────────────────────────────────────────┘
+            ↓ 最終検証
+┌─────────────────────────────────────────┐
+│   実際に適用される値 (自動調整)         │
+└─────────────────────────────────────────┘
+```
+
+---
+
+## 🔧 環境変数の設定
+
+### server/.env
+
+```env
+# チャンクサイズの上限 (tokens)
+# 使用する埋め込みモデルに合わせて設定
+# OpenAI text-embedding-3-large: 8191
+# OpenAI text-embedding-3-small: 8191
+# Google Gemini embedding-001: 2048
+MAX_CHUNK_SIZE=8191
+
+# チャンクオーバーラップの上限 (tokens)
+# チャンクサイズの 10-20% を推奨
+MAX_OVERLAP_SIZE=200
+```
+
+---
+
+## 🏗️ アーキテクチャの実装
+
+### 1. ChunkConfigService (バックエンドコア)
+
+```typescript
+// 環境変数から上限を読み込む
+private readonly envMaxChunkSize: number;
+private readonly envMaxOverlapSize: number;
+
+// 主要なモデルの制限
+private readonly MODEL_LIMITS = {
+  'text-embedding-3-large': {
+    maxInputTokens: 8191,
+    maxBatchSize: 2048,
+    expectedDimensions: 3072,
+  },
+  'embedding-001': {
+    maxInputTokens: 2048,
+    maxBatchSize: 100,
+    expectedDimensions: 768,
+  },
+};
+
+// 最終的な上限を計算
+const effectiveMaxChunkSize = Math.min(
+  this.envMaxChunkSize,      // 環境変数
+  limits.maxInputTokens      // モデルの制限
+);
+```
+
+### 2. 検証ロジック
+
+```typescript
+async validateChunkConfig(chunkSize, chunkOverlap, modelId, userId) {
+  const warnings = [];
+
+  // 1. 最終的な上限を計算
+  const effectiveMaxChunkSize = Math.min(
+    this.envMaxChunkSize,
+    limits.maxInputTokens
+  );
+
+  // 2. チャンクサイズの検証
+  if (chunkSize > effectiveMaxChunkSize) {
+    warnings.push(`上限 ${effectiveMaxChunkSize} を超えています`);
+    chunkSize = effectiveMaxChunkSize;
+  }
+
+  // 3. オーバーラップサイズの検証
+  const maxOverlap = Math.min(
+    this.envMaxOverlapSize,
+    Math.floor(chunkSize * 0.5)
+  );
+  if (chunkOverlap > maxOverlap) {
+    warnings.push(`オーバーラップが上限 ${maxOverlap} を超えています`);
+    chunkOverlap = maxOverlap;
+  }
+
+  return {
+    chunkSize,
+    chunkOverlap,
+    warnings,
+    effectiveMaxChunkSize,
+    effectiveMaxOverlapSize,
+  };
+}
+```
+
+### 3. API エンドポイント
+
+```typescript
+// GET /api/knowledge-bases/chunk-config/limits?embeddingModelId=xxx
+{
+  "maxChunkSize": 8191,
+  "maxOverlapSize": 200,
+  "defaultChunkSize": 200,
+  "defaultOverlapSize": 40,
+  "modelInfo": {
+    "name": "text-embedding-3-large",
+    "maxInputTokens": 8191,
+    "maxBatchSize": 2048,
+    "expectedDimensions": 3072
+  }
+}
+```
+
+---
+
+## 🎨 フロントエンドの実装
+
+### IndexingModal.tsx
+
+```typescript
+// 状態管理
+const [limits, setLimits] = useState(null);
+const [chunkSize, setChunkSize] = useState(200);
+const [chunkOverlap, setChunkOverlap] = useState(40);
+
+// モデル選択時に制限をロード
+useEffect(() => {
+  if (selectedEmbedding) {
+    const limitData = await chunkConfigService.getLimits(selectedEmbedding, token);
+    setLimits(limitData);
+
+    // 現在の値を自動調整
+    if (chunkSize > limitData.maxChunkSize) {
+      setChunkSize(limitData.maxChunkSize);
+    }
+  }
+}, [selectedEmbedding]);
+
+// スライダー変更時の処理
+const handleChunkSizeChange = (value) => {
+  if (limits && value > limits.maxChunkSize) {
+    showWarning(`最大値は ${limits.maxChunkSize} です`);
+    setChunkSize(limits.maxChunkSize);
+    return;
+  }
+  setChunkSize(value);
+
+  // オーバーラップの自動調整
+  if (chunkOverlap > value * 0.5) {
+    setChunkOverlap(Math.floor(value * 0.5));
+  }
+};
+```
+
+### UI 機能
+
+1. **動的なスライダー範囲**
+
+   ```jsx
+   <input
+     type="range"
+     min="50"
+     max={limits?.maxChunkSize || 8191}  // 動的な上限
+     value={chunkSize}
+     onChange={handleChunkSizeChange}
+   />
+   ```
+
+2. **制限のリアルタイム表示**
+
+   ```
+   チャンクサイズ: 200 tokens (上限: 8191)
+   ```
+
+3. **モデル情報の表示**
+
+   ```
+   モデル: text-embedding-3-large
+   チャンク上限: 8191 tokens
+   オーバーラップ上限: 200 tokens
+   バッチ制限: 2048
+   ```
+
+4. **最適化アドバイス**
+
+   ```
+   💡 最適化アドバイス
+   • チャンクが大きすぎます (800)。検索精度に影響する可能性があります。
+   • 少なくとも 80 tokens のオーバーラップを推奨します。
+   • 最大値を使用すると、処理速度が低下する可能性があります。
+   ```
+
+---
+
+## 📊 ユースケース例
+
+### シナリオ1: OpenAI + 環境変数による制限
+
+**設定:**
+
+```env
+MAX_CHUNK_SIZE=4000  # モデルより厳格なカスタム制限
+```
+
+**ユーザー操作:**
+
+```
+1. モデル選択: text-embedding-3-large
+2. スライダー上限: 4000 (環境変数による制限)
+3. ユーザー設定: 3000 tokens
+4. バックエンド検証: ✅ 合格
+5. 実適用値: 3000 tokens
+```
+
+### シナリオ2: Gemini + モデルによる制限
+
+**設定:**
+
+```env
+MAX_CHUNK_SIZE=8191  # 環境変数は緩和
+```
+
+**ユーザー操作:**
+
+```
+1. モデル選択: embedding-001
+2. スライダー上限: 2048 (モデル制限の方が厳格)
+3. ユーザー設定: 1500 tokens
+4. バックエンド検証: ✅ 合格
+5. 実適用値: 1500 tokens
+```
+
+### シナリオ3: 制限超過時の自動調整
+
+**ユーザー操作:**
+
+```
+1. モデル選択: embedding-001 (制限 2048)
+2. ユーザー入力: 3000 tokens
+3. フロントエンド表示: "最大値は 2048 です"
+4. スライダーを自動的に 2048 に調整
+5. バックエンド記録: ⚠️ 設定修正ログ
+```
+
+---
+
+## 🔍 優先順位ルール
+
+### 上限計算ロジック
+
+```typescript
+最終的な上限 = min(環境変数, モデル制限)
+
+例:
+- 環境変数: 8191
+- モデル制限: 2048 (Gemini)
+- 最終上限: 2048 ✅
+
+- 環境変数: 4000
+- モデル制限: 8191 (OpenAI)
+- 最終上限: 4000 ✅
+```
+
+### 検証順序
+
+```typescript
+1. チャンクサイズ ≤ 最終上限 かを確認
+2. チャンクサイズ ≥ 最小値 (50) かを確認
+3. オーバーラップサイズ ≤ 環境変数の上限 かを確認
+4. オーバーラップサイズ ≤ チャンクサイズの 50% かを確認
+5. オーバーラップサイズ ≥ 0 かを確認
+```
+
+---
+
+## 📝 デプロイ時の推奨設定
+
+### 開発環境
+
+```env
+# テストに適した設定
+MAX_CHUNK_SIZE=8191
+MAX_OVERLAP_SIZE=200
+```
+
+### 本番環境 (OpenAI)
+
+```env
+# 大容量ファイルへの対策を考慮した保守的な設定
+MAX_CHUNK_SIZE=4000
+MAX_OVERLAP_SIZE=500
+```
+
+### 本番環境 (Gemini)
+
+```env
+# モデルの制限に合わせた設定
+MAX_CHUNK_SIZE=2048
+MAX_OVERLAP_SIZE=300
+```
+
+---
+
+## ✅ メリットのまとめ
+
+| 特徴 | 実装方法 | 効果 |
+|------|----------|------|
+| **安全性** | 環境変数 + モデル制限の二重保護 | API の制限を超えない |
+| **柔軟性** | 環境変数で調整可能 | 異なるデプロイ要件に対応 |
+| **ユーザー体験** | フロントエンドでの動的制限 | 無効な値を選択できない |
+| **透明性** | 制限情報をリアルタイム表示 | 設定理由が明確 |
+| **自動調整** | バックエンドでの検証・修正 | 実行時のエラーを回避 |
+| **ログ管理** | 詳細な警告情報 | 問題の切り分けがスムーズ |
+
+---
+
+## 🎯 結論
+
+このスキームにより、以下が実現されました:
+
+1. ✅ **環境変数の設定** - グローバルに制御可能な上限
+2. ✅ **モデル制限の認識** - 異なるモデルの自動識別
+3. ✅ **フロントエンドでの制限** - 無効な値を選択不可に
+4. ✅ **バックエンド検証** - 二重の保険
+5. ✅ **自動調整** - 制限超過時の自動修正
+6. ✅ **透明なフィードバック** - 制限理由の表示
+
+**これで、ユーザーがモデルの制限を超える値を選択することはなくなり、システムが自動的に保護されます!**

+ 165 - 0
docs/CURRENT_IMPLEMENTATION.md

@@ -0,0 +1,165 @@
+# 現在の実装状況ドキュメント
+
+## システムアーキテクチャ
+
+### 技術スタック
+
+- **フロントエンド**: React + TypeScript + Vite + Tailwind CSS
+- **バックエンド**: NestJS + LangChain + Elasticsearch + TypeORM
+- **データベース**: SQLite (ユーザー、モデル設定、ナレッジベース、ユーザー設定)
+- **ベクトルストレージ**: Elasticsearch
+- **ファイル処理**: Apache Tika (高速モード) + Vision Pipeline (高精度モード)
+- **認証**: JWT
+- **ドキュメント変換**: LibreOffice + ImageMagick
+
+### コアモジュール
+
+#### 1. ユーザー認証システム (Auth)
+
+- JWT 認証システム
+- ユーザー登録/ログイン/パスワード変更
+- ユーザー管理画面
+- ルート保護と権限制御
+
+#### 2. モデル設定管理 (ModelConfig)
+
+- 多様なモデルプロバイダーをサポート:
+  - **OpenAI 互換**: OpenAI API および互換インターフェース(DeepSeek, Claude など)に対応
+  - **Google Gemini**: ネイティブ SDK によるサポート
+- モデルタイプ:
+  - **LLM**: 対話生成モデル
+  - **Embedding**: ベクトル化モデル
+  - **Rerank**: 再ランキングモデル
+- ユーザー独自の API キーとモデルパラメータの設定が可能
+- ビジョン機能のフラグ管理をサポート
+
+#### 3. ナレッジベース管理 (KnowledgeBase)
+
+- ファイルのアップロードと保存(日本語ファイル名に対応)
+- **デュアルモード処理**:
+  - **高速モード**: Apache Tika によるテキスト抽出
+  - **高精度モード**: Vision Pipeline による画像・テキスト混合処理
+- インテリジェントなドキュメントのチャンク分割とベクトル化
+- Elasticsearch インデックスとハイブリッド検索
+- ファイルステータス管理(待機中、インデックス中、完了、失敗)
+- 数百種類のファイル形式をサポート: PDF, Word, PPT, Excel, Markdown, コードファイル, 画像など
+
+#### 4. RAG 質問応答システム (Chat)
+
+- **ストリーミング出力**: Server-Sent Events (SSE) を利用
+- **インテリジェント検索**: LangChain キーワード抽出 + ES ハイブリッド検索
+- **類似度フィルタリング**: 関連性の低いコンテンツをフィルタリングするしきい値を設定可能
+- **引用表示**: ソースの断片、ファイル名、類似度スコアを表示
+- **多言語サポート**: ユーザーの言語設定に合わせて AI の回答言語を調整
+
+#### 5. 統合設定管理 (Unified Settings)
+
+- **統合設定モーダル**: 各種管理機能を一つのタブ形式インターフェースに集約
+  - **一般設定 (General)**: 言語切り替え、パスワード変更
+  - **ユーザー管理 (User)**: ユーザー一覧、ユーザー追加(管理者機能)
+  - **モデル管理 (Model)**: LLM, Embedding, Rerank モデルの設定と管理
+- **クイックアクセス**: サイドバー下部の「設定」ボタンからワンクリックでアクセス可能
+- **一貫した体験**: 統一されたフォーム操作とステータスフィードバック
+
+## チャットフロー
+
+```
+ユーザーの質問 → キーワード抽出 → ESハイブリッド検索 → 類似度フィルタリング → コンテキスト構築 → LLMストリーミング生成 → 引用元の表示
+```
+
+### 詳細ステップ
+
+1. **キーワード抽出**
+   - LangChain を使用して、ユーザーの質問から 3-5 個のキーワードを抽出します。
+   - 不要な言葉(「の」「は」「のぼり」など)を除去します。
+
+2. **ハイブリッド検索**
+   - キーワードを組み合わせてベクトル検索を実行します。
+   - 全文検索を併用して再現率を向上させます。
+   - 類似度しきい値でフィルタリングします。
+   - 最も関連性の高いセグメントを返します。
+
+3. **ストリーミング生成**
+   - 処理の進捗を表示します(「ナレッジベースを検索中...」など)。
+   - LLM が生成した回答内容をリアルタイムで出力します。
+   - 取得したセグメントに基づき、引用付きの回答を生成します。
+   - 多言語での回答をサポートします。
+
+4. **引用元の表示**
+   - セグメント内容の要約(最大150文字)を表示します。
+   - 出典元ファイル名を表示します。
+   - 類似度のパーセンテージを表示します。
+   - セグメント番号を表示します。
+
+## 主要な API エンドポイント
+
+### 認証関連
+
+- `POST /auth/login` - ログイン
+- `POST /auth/register` - ユーザー登録
+- `POST /auth/change-password` - パスワード変更(設定モーダル内から呼び出し)
+
+### チャット API
+
+- `POST /chat/stream` - ストリーミングチャット
+  - ユーザーが設定した LLM モデルと API キーを自動取得
+  - OpenAI 互換インターフェースと Gemini をサポート
+  - SSE ストリーミングレスポンスを返却
+  - 多言語パラメータの受け渡しに対応
+
+### モデル管理
+
+- `GET /model-configs` - モデル設定の一覧取得
+- `POST /model-configs` - モデル設定の作成
+- `PUT /model-configs/:id` - モデル設定の更新
+- `DELETE /model-configs/:id` - モデル設定の削除
+
+### ナレッジベース管理
+
+- `POST /upload` - ファイルアップロード(チャンクパラメータの設定が可能)
+- `GET /knowledge-bases` - ファイル一覧の取得
+- `DELETE /knowledge-bases/:id` - ファイルの削除
+- `DELETE /knowledge-bases/clear` - ナレッジベースの全消去
+
+### ユーザー設定
+
+- `GET /user-settings` - 設定の取得
+- `PUT /user-settings` - 設定の更新
+- **注**: フロントエンドは統一された `SettingsModal` コンポーネントを介してこれらのエンドポイントと通信します。
+
+## 利用方法
+
+1. **ユーザー登録/ログイン**
+2. **基本構成の設定**
+   - サイドバー下部の「設定」ボタンをクリックします。
+   - 「モデル管理」タブで OpenAI 互換の LLM と Embedding モデルを追加します。
+   - API キーとモデルパラメータを設定します。
+   - 「一般設定」タブで言語を切り替えたり、パスワードを変更したりできます。
+3. **ドキュメントのアップロード**
+   - PDF, テキスト, 画像などの形式をサポートします。
+   - チャンクサイズと Embedding モデルを設定します。
+   - 自動的にベクトル化とインデックス化が行われます。
+4. **詳細設定の調整**
+   - 使用するモデルを選択します。
+   - 推論パラメータと検索パラメータを構成します。
+   - UI 言語を設定します。
+5. **対話を開始**
+   - 質問を送信します。
+   - ストリーミング処理の経過を観察します。
+   - 引用付きのインテリジェントな回答を確認します。
+
+## 特筆すべき機能
+
+- ✅ **ユーザー分離**: 各ユーザーに独立したモデル設定とナレッジベースを提供
+- ✅ **ストリーミング体験**: 処理の進捗と生成内容をリアルタイム表示
+- ✅ **インテリジェント検索**: キーワード抽出 + ハイブリッド検索 + 類似度フィルタリング
+- ✅ **引用追跡**: 回答の根拠となるソースと関連セグメントを明確に表示
+- ✅ **マルチモデル対応**: OpenAI 互換インターフェース + Gemini ネイティブサポート
+- ✅ **柔軟な設定**: ユーザー独自の API キーと推論パラメータのカスタマイズ
+- ✅ **多言語サポート**: UI と AI 回答の両方を完全国際化
+- ✅ **ビジョン機能**: 画像処理をサポートするマルチモーダルモデルに対応
+- ✅ **ユーザー管理**: 登録、ログイン、パスワード管理の完備
+- ✅ **デュアルモード処理**: 高速モード (テキストのみ) + 高精度モード (画像・テキスト混合)
+- ✅ **メモリの最適化**: 大容量ファイルを分割処理し、メモリオーバーフローを防止
+- ✅ **統合管理**: モデル、ユーザー、一般設定を一つにまとめたモダンな UI
+- ✅ **ミニマルなデザイン**: サイドバーとヘッダーの冗余を排除し、対話体験に集中

+ 444 - 0
docs/DEPLOYMENT.md

@@ -0,0 +1,444 @@
+# デプロイガイド
+
+## 開発環境のデプロイ
+
+### 前提条件
+
+- Node.js 18+
+- Yarn
+- Docker & Docker Compose
+
+### 1. プロジェクトのクローン
+
+```bash
+git clone <repository-url>
+cd lumina
+```
+
+### 2. 依存関係のインストール
+
+```bash
+yarn install
+```
+
+### 3. 基本サービスの起動
+
+```bash
+# Elasticsearch と Tika を起動
+docker-compose up -d elasticsearch tika
+```
+
+### 4. 環境変数の設定
+
+```bash
+# 環境変数のテンプレートをコピー
+cp server/.env.sample server/.env
+
+# 設定ファイルを編集
+vim server/.env
+```
+
+### 5. 開発サービスの起動
+
+```bash
+# フロントエンドとバックエンドを同時に起動
+yarn dev
+
+# または個別に起動
+yarn dev:web    # フロントエンド (ポート 5173)
+yarn dev:server # バックエンド (ポート 3000)
+```
+
+## 本番環境のデプロイ
+
+### Docker Compose を使用する場合
+
+1. **環境変数の設定**
+
+```bash
+cp .env.sample .env
+# 本番環境の設定を編集
+```
+
+1. **ビルドと起動**
+
+```bash
+# すべてのサービスを一括起動
+docker-compose up -d
+```
+
+1. **サービスへのアクセス**
+
+- HTTPS: <https://localhost> (推奨)
+- HTTP: <http://localhost> (HTTPS へ自動リダイレクト)
+- バックエンド API: Nginx プロキシ経由でアクセス
+- Elasticsearch: <http://localhost:9200>
+- Tika: <http://localhost:9998>
+
+### サービス構成
+
+- **nginx**: リバースプロキシ。HTTPS と CORS 処理を担当
+- **web**: フロントエンド静的ファイルサービス (React)
+- **server**: バックエンド API サービス (NestJS)
+- **libreoffice**: LibreOffice ドキュメント変換サービス (FastAPI, ポート 8100)
+- **elasticsearch**: ベクトル検索エンジン (ポート 9200)
+- **tika**: ドキュメント内容抽出サービス (ポート 9998)
+
+### アーキテクチャ図
+
+```
+ユーザー → nginx → web (React)
+                ↓
+              server (NestJS) → elasticsearch
+                ↓                 ↓
+          libreoffice        tika
+          (FastAPI)       (Java)
+```
+
+### SSL 証明書の設定
+
+#### 自己署名証明書(開発環境用)
+
+```bash
+# 自己署名証明書を生成
+./nginx/generate-ssl.sh
+```
+
+#### 本番環境用証明書
+
+正式な SSL 証明書を以下の場所に配置してください:
+
+- 証明書ファイル: `nginx/ssl/cert.pem`
+- 秘密鍵ファイル: `nginx/ssl/key.pem`
+
+### Docker 常用コマンド
+
+```bash
+# すべてのサービスのログを表示
+docker-compose logs -f
+
+# 特定のサービスのログを表示
+docker-compose logs -f nginx
+docker-compose logs -f server
+
+# サービスの再起動
+docker-compose restart
+
+# 特定のサービスの再起動
+docker-compose restart nginx
+
+# サービスの停止
+docker-compose down
+
+# 再ビルドして起動
+docker-compose up -d --build
+```
+
+### データの永続化
+
+#### SQLite データベース
+
+- データベースファイルの場所: `./data/database.sqlite`
+- コンテナ外部に自動マウントされるため、コンテナ再起動時もデータは失われません。
+
+#### アップロードファイル
+
+- ファイルの保存場所: `./uploads/`
+- アップロードされたすべてのドキュメントと画像はホストマシンに保存されます。
+
+#### Elasticsearch データ
+
+- データボリューム: `elasticsearch-data`
+- ベクトルインデックスデータが永続的に保存されます。
+
+### 手動デプロイ
+
+1. **フロントエンドのビルド**
+
+```bash
+cd web
+yarn build
+```
+
+1. **バックエンドのビルド**
+
+```bash
+cd server
+yarn build
+```
+
+1. **Nginx の設定**
+
+```nginx
+server {
+    listen 80;
+    server_name your-domain.com;
+    
+    # フロントエンド静的ファイル
+    location / {
+        root /path/to/web/dist;
+        try_files $uri $uri/ /index.html;
+    }
+    
+    # バックエンド API プロキシ
+    location /api {
+        proxy_pass http://localhost:3000;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+    }
+}
+```
+
+## 環境変数の設定
+
+### バックエンド環境変数 (server/.env)
+
+```bash
+# データベース
+DATABASE_PATH=./data/metadata.db
+
+# Elasticsearch
+ELASTICSEARCH_HOST=http://localhost:9200
+ELASTICSEARCH_INDEX=knowledge_base_chunks
+
+# Tika サービス
+TIKA_HOST=http://localhost:9998
+
+# LibreOffice サービス
+LIBREOFFICE_URL=http://localhost:8100
+
+# Vision API
+VISION_API_KEY=sk-xxx-your-key
+VISION_API_BASE=https://api.openai.com/v1
+VISION_MODEL=gpt-4-vision-preview
+
+# ファイルアップロード
+UPLOAD_FILE_PATH=./uploads
+MAX_FILE_SIZE=50MB
+
+# 一時ディレクトリ
+TEMP_DIR=./temp
+
+# JWT
+JWT_SECRET=your-jwt-secret-key
+JWT_EXPIRES_IN=7d
+
+# サービスポート
+PORT=3000
+```
+
+### フロントエンド環境変数 (web/.env)
+
+```bash
+# API アドレス
+VITE_API_BASE_URL=http://localhost:3000
+
+# アプリケーション設定
+VITE_APP_TITLE=Lumina
+VITE_MAX_FILE_SIZE=50
+```
+
+## バックアップと復元
+
+### SQLite データベースのバックアップ
+
+```bash
+# バックアップ
+cp server/data/metadata.db backup/metadata_$(date +%Y%m%d).db
+
+# 復元
+cp backup/metadata_20240101.db server/data/metadata.db
+```
+
+### Elasticsearch データのバックアップ
+
+```bash
+# スナップショット用リポジトリの作成
+curl -X PUT "localhost:9200/_snapshot/backup_repo" -H 'Content-Type: application/json' -d'
+{
+  "type": "fs",
+  "settings": {
+    "location": "/backup/elasticsearch"
+  }
+}'
+
+# スナップショットの作成
+curl -X PUT "localhost:9200/_snapshot/backup_repo/snapshot_1"
+```
+
+## 監視とログ
+
+### アプリケーションログ
+
+- フロントエンドログ: ブラウザのコンソール
+- バックエンドログ: `server/logs/`
+- Elasticsearch ログ: Docker コンテナログ
+
+### ヘルスチェック
+
+```bash
+# バックエンドのヘルスチェック
+curl http://localhost:3000/health
+
+# Elasticsearch のヘルスチェック
+curl http://localhost:9200/_cluster/health
+```
+
+## トラブルシューティング
+
+### よくある質問
+
+1. **Elasticsearch への接続に失敗する**
+   - Docker コンテナの状態を確認してください。
+   - ポート 9200 がアクセス可能か確認してください。
+   - ファイアウォールの設定を確認してください。
+
+2. **ファイルのアップロードに失敗する**
+   - uploads ディレクトリの権限を確認してください。
+   - Tika サービスが正常に動作しているか確認してください。
+   - ファイルサイズの制限を確認してください。
+
+3. **モデル API の呼び出しに失敗する**
+   - API キーの設定を確認してください。
+   - ネットワーク接続を確認してください。
+   - モデル ID が正しいか確認してください。
+
+#### 1. Elasticsearch への接続失敗
+
+**症状**: バックエンドログに "Connection refused" または "ECONNREFUSED" と表示される
+**解決策**:
+
+- Docker コンテナの状態確認: `docker-compose ps`
+- ポート 9200 のアクセス確認: `curl http://localhost:9200`
+- ファイアウォール設定の確認
+- Elasticsearch の再起動: `docker-compose restart elasticsearch`
+
+#### 2. ファイルのアップロード失敗
+
+**症状**: アップロード時に「アップロード失敗」または「処理失敗」と表示される
+**解決策**:
+
+- uploads ディレクトリの権限確認: `ls -la server/uploads/`
+- Tika サービスの状態確認: `curl http://localhost:9998/version`
+- ファイルサイズ制限の確認(デフォルト 50MB)
+- バックエンドログの詳細エラーを確認
+
+#### 3. モデル API の呼び出し失敗
+
+**症状**: チャット時に「API キーが無効」または「モデルの呼び出しに失敗」と表示される
+**解決策**:
+
+- API キーの設定が正しいか確認
+- ネットワーク接続とファイアウォールの確認
+- モデル ID の確認(例: gpt-4, gpt-3.5-turbo)
+- API の利用残高と権限の確認
+- Base URL の設定確認(OpenAI 互換インターフェースの場合)
+
+#### 4. ユーザー認証の問題
+
+**症状**: ログイン失敗またはトークンの期限切れ
+**解決策**:
+
+- ユーザー名とパスワードの確認
+- ブラウザのキャッシュと localStorage のクリア
+- JWT_SECRET の設定確認
+- ユーザーアカウントの再登録
+
+#### 5. ナレッジベースの検索結果が出ない
+
+**症状**: 質問後に「関連する知識が見つかりません」と表示される
+**解決策**:
+
+- ファイルのインデックスが完了しているか確認(ステータスが「完了」)
+- 類似度しきい値の設定を調整
+- Embedding モデルの設定確認
+- 別のキーワードで質問してみる
+
+#### 6. フロントエンドページにアクセスできない
+
+**症状**: ブラウザに「このサイトにアクセスできません」と表示される
+**解決策**:
+
+- フロントエンドサービスがポート 5173 で動作しているか確認
+- ファイアウォールとプロキシの設定確認
+- ブラウザのキャッシュのクリア
+- シークレットモードでのアクセスを試す
+
+### デバッグツール
+
+#### サービス状態の確認
+
+```bash
+# すべての Docker コンテナを確認
+docker-compose ps
+
+# ポートの使用状況を確認
+netstat -tulpn | grep :5173
+netstat -tulpn | grep :3000
+```
+
+#### 詳細ログの表示
+
+```bash
+# バックエンドログ
+docker-compose logs -f server
+
+# Elasticsearch ログ
+docker-compose logs -f elasticsearch
+
+# フロントエンド開発ログ
+cd web && yarn dev
+```
+
+#### ヘルスチェック
+
+```bash
+# バックエンド API のヘルスチェック
+curl http://localhost:3000/health
+
+# Elasticsearch のヘルスチェック
+curl http://localhost:9200/_cluster/health
+
+# LibreOffice サービスのチェック
+curl http://localhost:8100/health
+
+# LibreOffice API ドキュメントの表示
+open http://localhost:8100/docs
+```
+
+### パフォーマンスの最適化
+
+#### 1. Elasticsearch のチューニング
+
+```bash
+# JVM ヒープメモリの増量
+export ES_JAVA_OPTS="-Xms2g -Xmx2g"
+
+# インデックス状態の確認
+curl http://localhost:9200/_cat/indices?v
+```
+
+#### 2. ファイル処理の最適化
+
+- 同時にアップロードするファイル数を制限する
+- チャンクサイズを適切に調整する(推奨 512-1024 トークン)
+- 不要なインデックスデータを定期的に整理する
+
+### データの復元
+
+#### SQLite データベースの復元
+
+```bash
+# バックアップから復元
+cp backup/metadata_20240101.db server/data/metadata.db
+
+# データベースの整合性チェック
+sqlite3 server/data/metadata.db "PRAGMA integrity_check;"
+```
+
+#### Elasticsearch データの復元
+
+```bash
+# スナップショットの復元
+curl -X POST "localhost:9200/_snapshot/backup_repo/snapshot_1/_restore"
+```

+ 217 - 0
docs/DESIGN.md

@@ -0,0 +1,217 @@
+# Lumina (簡易ナレッジベース) - システム設計ドキュメント
+
+## 1. プロジェクト概要
+
+本プロジェクトは、React + NestJS をベースに開発されたフルスタックのナレッジベースQ&Aシステム(RAG - Retrieval-Augmented Generation)です。
+ユーザーは多様な形式のドキュメントをアップロードし、チャンク分割やインデックス設定をカスタマイズした上で、大規模言語モデル(LLM)を利用してナレッジベースに基づいた質問応答を行うことができます。
+
+---
+
+## 2. コアアーキテクチャ設計
+
+### 2.1 技術スタック
+
+- **フロントエンド**: React 19 + TypeScript + Vite + Tailwind CSS
+- **バックエンド**: NestJS + LangChain + TypeORM
+- **データベース**: SQLite (ユーザー、モデル設定、ナレッジベースのメタデータ)
+- **ベクトルストレージ**: Elasticsearch
+- **ファイル処理**: Apache Tika (高速モード) + Vision Pipeline (高精度モード)
+- **認証**: JWT
+- **ドキュメント変換**: LibreOffice + ImageMagick
+
+### 2.2 システムアーキテクチャ
+
+```
+ユーザー -> Reactフロントエンド -> NestJSバックエンド -> SQLite/Elasticsearch
+                        |
+                        v
+                   LangChain Agent
+                        |
+                        v
+                   大言語モデル (LLM)
+
+高精度モードのプロセス:
+PDF/Word/PPT -> LibreOffice -> PDF -> ImageMagick -> Vision Model -> 構造化コンテンツ
+```
+
+---
+
+## 3. コア機能モジュール
+
+### 3.1 ユーザー認証システム
+
+- **JWT 認証**: ユーザー名/パスワードに基づくログインシステム
+- **ユーザー管理**: ユーザー登録、パスワード変更をサポート
+- **権限制御**: ユーザーデータの隔離。各ユーザーに独立したナレッジベースを提供
+- **管理画面**: 統合された設定モーダルによるユーザー管理(作成/リスト表示)
+
+### 3.2 モデル設定管理
+
+- **マルチプロバイダー対応**:
+  - **OpenAI 互換**: OpenAI API および互換インターフェース(DeepSeek, Claude など)に対応
+  - **Google Gemini**: ネイティブ SDK によるサポート
+- **モデルタイプ**:
+  - **LLM**: 対話生成モデル
+  - **Embedding**: ベクトル化モデル  
+  - **Rerank**: 再ランキングモデル
+- **ユーザーカスタマイズ**: ユーザーが独自の API キーとモデルパラメータを設定可能
+- **ビジョンサポート**: モデルが画像処理に対応しているかどうかのフラグ管理
+- **統合管理**: 設定モーダルの「モデル管理」タブで一括管理
+
+### 3.3 ナレッジベース管理
+
+- **ファイルアップロード**: PDF、ドキュメント、画像など多様な形式に対応
+- **デュアルモード処理**:
+  - **高速モード**: Apache Tika を使用したテキスト抽出
+  - **高精度モード**: Vision Pipeline を使用した画像・テキスト混合処理
+- **インテリジェント・チャンキング**: チャンクサイズとオーバーラップを調整可能
+- **ベクトルインデックス**: ユーザーが選択した Embedding モデルによるベクトル化
+- **ステータス管理**: ファイル処理状況の追跡(待機中、インデックス中、完了、失敗)
+
+### 3.4 RAG 質問応答システム
+
+- **インテリジェント検索**:
+  - キーワード抽出とクエリの最適化
+  - ハイブリッド検索(ベクトル検索 + 全文検索)
+  - 類似度しきい値によるフィルタリング
+- **ストリーミング生成**:
+  - Server-Sent Events (SSE) によるストリーミング出力
+  - 処理の進捗をリアルタイムに表示
+  - 生成コンテンツを1文字ずつ表示
+- **引用追跡**:
+  - 回答の出典ファイルを表示
+  - 関連するドキュメントセグメントを表示
+  - 類似度スコアの表示
+
+### 3.5 多言語サポート
+
+- **インターフェースの国際化**: 日本語、中国語、英語に対応
+- **インテリジェント回答**: ユーザーの言語設定に基づいた AI による回答
+- **言語設定の永続化**: ユーザーの選択した言語を自動保存
+
+---
+
+## 4. データモデル設計
+
+### 4.1 SQLite テーブル
+
+**ユーザー表 (users)**
+
+- id, username, password_hash, is_admin, created_at
+
+**モデル設定表 (model_configs)**  
+
+- id, user_id, name, provider, model_id, base_url, api_key, type, supports_vision
+
+**ナレッジベースファイル表 (knowledge_bases)**
+
+- id, user_id, name, original_name, storage_path, size, mimetype, status, created_at
+
+**ユーザー設定表 (user_settings)**
+
+- user_id, selected_llm_id, selected_embedding_id, temperature, max_tokens, top_k, similarity_threshold, language
+
+### 4.2 Elasticsearch インデックス
+
+**ナレッジベース・チャンクインデックス (knowledge_base_chunks)**
+
+```json
+{
+  "chunk_id": "string",
+  "knowledge_base_id": "string", 
+  "user_id": "string",
+  "file_name": "string",
+  "content": "text",
+  "embedding": "dense_vector[1536]",
+  "chunk_index": "integer",
+  "metadata": "object"
+}
+```
+
+---
+
+## 5. API エンドポイント設計
+
+### 5.1 認証
+
+- `POST /auth/login` - ログイン
+- `POST /auth/register` - ユーザー登録  
+- `POST /auth/change-password` - パスワード変更
+
+### 5.2 モデル管理
+
+- `GET /model-configs` - モデル設定の取得
+- `POST /model-configs` - モデル設定の作成
+- `PUT /model-configs/:id` - モデル設定の更新
+- `DELETE /model-configs/:id` - モデル設定の削除
+
+### 5.3 ナレッジベース
+
+- `POST /upload` - ファイルアップロード
+- `GET /knowledge-bases` - ファイル一覧の取得
+- `DELETE /knowledge-bases/:id` - ファイルの削除
+- `DELETE /knowledge-bases/clear` - ナレッジベースの全削除
+
+### 5.4 チャット
+
+- `POST /chat/stream` - ストリーミングチャット(SSE)
+
+### 5.5 ユーザー設定
+
+- `GET /user-settings` - 設定の取得
+- `PUT /user-settings` - 設定の更新
+
+---
+
+## 6. コアフロー
+
+### 6.1 ファイル処理フロー
+
+**高速モード**:
+
+```
+アップロード → メタデータ保存 → Tikaテキスト抽出 → チャンク分割 → ベクトル化 → ESインデックス → ステータス更新
+```
+
+**高精度モード**:
+
+```
+アップロード → LibreOffice変換 → PDF画像化 → Vision分析 → 構造化コンテンツ → ベクトル化 → ESインデックス
+```
+
+### 6.2 RAG 質問応答フロー  
+
+```
+ユーザーの質問 → キーワード抽出 → ハイブリッド検索 → 類似度フィルタリング → プロンプト構築 → LLM生成 → ストリーミング出力 → 引用表示
+```
+
+---
+
+## 7. デプロイ構成
+
+### 7.1 開発環境
+
+- フロントエンド: Vite 開発サーバー (ポート 5173)
+- バックエンド: NestJS 開発サーバー (ポート 3000)  
+- Elasticsearch: Docker コンテナ (ポート 9200)
+- Apache Tika: Docker コンテナ (ポート 9998)
+
+### 7.2 本番環境
+
+- Docker Compose を使用したコンテナ化デプロイ
+- Nginx によるリバースプロキシと負荷分散
+- SSL 証明書の設定
+
+---
+
+## 8. 特徴的な機能
+
+- ✅ **ユーザー分離**: ユーザーごとに独立したモデル設定とナレッジベースを保持
+- ✅ **ストリーミング体験**: 処理状況と生成内容をリアルタイム表示  
+- ✅ **インテリジェント検索**: キーワード抽出 + ハイブリッド検索 + 類似度フィルタリング
+- ✅ **引用追跡**: 回答の根拠となるソースと関連セグメントを明確に表示
+- ✅ **マルチモデル対応**: OpenAI 互換インターフェース + Gemini ネイティブサポート
+- ✅ **柔軟な設定**: ユーザー独自の API キーと推論パラメータのカスタマイズ
+- ✅ **多言語対応**: UI と AI 回答の完全な国際化サポート
+- ✅ **ビジョン機能**: 画像処理に対応したマルチモーダルモデルのサポート
+- ✅ **デュアルモード処理**: 高速モード (テキストのみ) + 高精度モード (画像・テキスト混合)

+ 71 - 0
docs/DEVELOPMENT_STANDARDS.md

@@ -0,0 +1,71 @@
+# 開発基準
+
+## コードコメントの基準
+
+### 1. コメントの言語
+
+- **すべてのコードコメントは中国語を使用する必要があります**
+- 以下を含みますが、これらに限定されません:
+  - 関数/メソッドのコメント
+  - 行内コメント
+  - コードブロックの説明
+  - TODO/FIXME コメント
+
+### 2. ログ出力の基準
+
+- **すべてのログ出力は中国語を使用する必要があります**
+- 以下を含みますが、これらに限定されません:
+  - `logger.log()` 情報ログ
+  - `logger.warn()` 警告ログ
+  - `logger.error()` ラーログ
+  - `console.log()` デバッグ出力
+
+### 3. エラーメッセージの基準
+
+- **ユーザーに表示されるエラーメッセージは中国語を使用します**
+- **開発デバッグ用のエラーメッセージは中国語を使用します**
+- 例外スロー時のエラーメッセージには中国語を使用します
+
+## 例
+
+### 正しいコメントとログ
+
+```typescript
+// 正解:中国語のコメント
+async getEmbeddings(texts: string[]): Promise<number[][]> {
+  this.logger.log(`正在为 ${texts.length} 个文本生成嵌入向量`); // 正解:中国語のログ
+  
+  try {
+    // APIを呼び出して埋め込みベクトルを取得
+    const response = await this.callEmbeddingAPI(texts);
+    return response.data;
+  } catch (error) {
+    this.logger.error('获取嵌入向量失败', error); // 正解:中国語のログ
+    throw new Error('嵌入向量生成失败'); // 正解:中国語のエラーメッセージ
+  }
+}
+```
+
+### 誤ったコメントとログ
+
+```typescript
+// 誤り:英語のコメントとログ
+async getEmbeddings(texts: string[]): Promise<number[][]> {
+  this.logger.log(`Getting embeddings for ${texts.length} texts`);
+  
+  try {
+    // Call API to get embeddings
+    const response = await this.callEmbeddingAPI(texts);
+    return response.data;
+  } catch (error) {
+    this.logger.error('Failed to get embeddings', error);
+    throw new Error('Embedding generation failed');
+  }
+}
+```
+
+## 履行基準
+
+1. **コードレビュー時には、必ずコメントとログの言語をチェックしてください**
+2. **新規コードは、中国語のコメントとログの基準に従う必要があります**
+3. **既存のコードをリファクタリングする際は、同時にコメントとログも中国語に更新してください**

+ 219 - 0
docs/EMBEDDING_MODEL_ID_FIX.md

@@ -0,0 +1,219 @@
+# Embedding モデル ID 連携の修正
+
+## 🐛 問題の記述
+
+```
+混合検索失敗: NotFoundException: ModelConfig with ID "embedding-3" not found or not owned by user.
+```
+
+## 🔍 問題の分析
+
+### 混同されやすい概念
+
+システム内には2種類の異なる「ID」が存在します:
+
+1. **モデル設定テーブルの ID** (`ModelConfig.id`)
+   - データベースの主キー
+   - 例:`"embedding-3"`, `"default-embedding"`
+   - 用途:`ModelConfigService.findOne(id, userId)`
+
+2. **モデル識別子** (`ModelConfig.modelId`)
+   - AI ベンダー側でのモデル名
+   - 例:`"text-embedding-3-large"`, `"text-embedding-ada-002"`
+   - 用途:AI API 呼び出し時のパラメータ
+
+### データフロー
+
+```
+ユーザー設定: user_setting.selectedEmbeddingId = "embedding-3"  ✅ テーブルID
+
+フロントエンド: settings.selectedEmbeddingId
+    ↓ 転送
+バックエンド Controller: selectedEmbeddingId = "embedding-3"
+    ↓ 転送
+ChatService: embeddingModel.id = "embedding-3"  ✅ 正常
+    ↓ 転送
+hybridSearch: embeddingModelId = "embedding-3"
+    ↓ 転送
+EmbeddingService.getEmbeddings(embeddingModelId)
+    ↓ 呼び出し
+ModelConfigService.findOne("embedding-3", userId)  ✅ 正常
+```
+
+### 以前の誤り
+
+**ChatService.ts (誤り):**
+
+```typescript
+// 182行目付近
+searchResults = await this.hybridSearch(
+  [message],
+  userId,
+  embeddingModel.modelId,  // ❌ 誤り! "text-embedding-3-large" を渡してしまっていた
+);
+```
+
+**hybridSearch (受信側):**
+
+```typescript
+private async hybridSearch(
+  keywords: string[],
+  userId: string,
+  embeddingModelId?: string,  // "text-embedding-3-large" を受け取ってしまう
+)
+```
+
+**EmbeddingService (期待値):**
+
+```typescript
+async getEmbeddings(
+  texts: string[],
+  userId: string,
+  embeddingModelConfigId: string,  // 本来は "embedding-3" を期待
+) {
+  const modelConfig = await this.modelConfigService.findOne(
+    embeddingModelConfigId,  // ❌ "text-embedding-3-large" で検索しても見つからない!
+    userId,
+  );
+}
+```
+
+## ✅ 修正内容
+
+### 修正箇所
+
+**server/src/chat/chat.service.ts:**
+
+```typescript
+// 182行目付近
+searchResults = await this.hybridSearch(
+  [message],
+  userId,
+  embeddingModel.id,  // ✅ テーブルID "embedding-3" を使用するように変更
+);
+```
+
+### 修正後のフロー
+
+```
+1. ユーザーが埋め込みモデルを選択: text-embedding-3-large
+   ↓
+2. システムが user_setting テーブルに保存:
+   selectedEmbeddingId = "embedding-3"  (ModelConfig テーブルの主キー)
+   ↓
+3. フロントエンドがチャットリクエストを送信:
+   { selectedEmbeddingId: "embedding-3" }
+   ↓
+4. バックエンド Controller が受信:
+   selectedEmbeddingId = "embedding-3"
+   ↓
+5. ChatService がモデルを検索:
+   embeddingModel = models.find(m => m.id === "embedding-3")
+   // 結果: { id: "embedding-3", modelId: "text-embedding-3-large", ... }
+   ↓
+6. ChatService が hybridSearch を呼び出し:
+   hybridSearch(..., embeddingModel.id)  // "embedding-3" を渡す
+   ↓
+7. hybridSearch が EmbeddingService を呼び出し:
+   getEmbeddings(..., "embedding-3")
+   ↓
+8. EmbeddingService が設定を検索:
+   findOne("embedding-3", userId)  // ✅ 設定が見つかる
+   ↓
+9. AI API を呼び出し:
+   model: "text-embedding-3-large"  // modelId を用いて API を実行
+```
+
+## 📊 ID の対応関係
+
+| シーン | 使用するフィールド | 例 | 用途 |
+|------|-----------|--------|------|
+| ユーザー設定 | `user_setting.selectedEmbeddingId` | `"embedding-3"` | ユーザーの選択を保存 |
+| 設定の検索 | `ModelConfig.id` | `"embedding-3"` | データベースクエリ |
+| API 呼び出し | `ModelConfig.modelId` | `"text-embedding-3-large"` | AI ベンダーのインターフェース |
+
+## 🔑 重要な原則
+
+### 1. データベース操作にはテーブル ID を使用する
+
+```typescript
+// ✅ 正解
+const model = await modelConfigService.findOne(modelId, userId);  // modelId = "embedding-3"
+
+// ❌ 誤り
+const model = await modelConfigService.findOne(modelId, userId);  // modelId = "text-embedding-3-large"
+```
+
+### 2. API 呼び出しにはモデル識別子を使用する
+
+```typescript
+// ✅ 正解
+fetch(apiUrl, {
+  body: JSON.stringify({
+    model: modelConfig.modelId,  // "text-embedding-3-large"
+  }),
+});
+```
+
+### 3. 内部的な受け渡しにはテーブル ID を使用する
+
+```typescript
+// ✅ 正解
+embeddingService.getEmbeddings(texts, userId, modelConfig.id);  // "embedding-3"
+
+// ❌ 誤り
+embeddingService.getEmbeddings(texts, userId, modelConfig.modelId);  // "text-embedding-3-large"
+```
+
+## 🧪 検証
+
+### テスト手順
+
+1. **ユーザー設定の確認**
+
+   ```sql
+   SELECT selectedEmbeddingId FROM user_setting WHERE userId = 'xxx';
+   -- 期待値: "embedding-3" (テーブルID)
+   ```
+
+2. **モデル設定の確認**
+
+   ```sql
+   SELECT id, modelId, name FROM model_config WHERE userId = 'xxx';
+   -- 期待値: embedding-3 | text-embedding-3-large | Text Embedding 3 Large
+   ```
+
+3. **チャットメッセージの送信**
+   - バックエンドログを確認
+   - 期待される出力: "使用嵌入模型: Text Embedding 3 Large text-embedding-3-large ID: embedding-3"
+
+4. **埋め込みベクトルの生成確認**
+   - ログに "从 Text Embedding 3 Large 获取到 X 个嵌入向量" と表示されること
+
+### 期待されるログ出力
+
+```
+=== ChatService.streamChat ===
+User ID: user-123
+Selected Embedding ID: embedding-3
+ID に基づいてモデルを検索: embedding-3
+使用するモデル: Text Embedding 3 Large text-embedding-3-large ID: embedding-3
+埋め込みベクトルを生成中...
+Text Embedding 3 Large から 1 個の埋め込みベクトルを取得しました。次元数: 2560
+```
+
+## 📁 修正されたファイル
+
+- `server/src/chat/chat.service.ts` - 182行目。 `embeddingModel.modelId` ではなく `embeddingModel.id` を渡すように変更。
+
+## 💡 学んだ教訓
+
+1. **2種類の ID を区別すること**:テーブル主キー vs モデル識別子
+2. **パラメータ名を明確にすること**:`embeddingModelConfigId` vs `embeddingModelId`
+3. **呼び出し先の期待値を確認すること**:`EmbeddingService` がどのタイプの ID を求めているか
+4. **ログ出力の工夫**:デバッグを容易にするため、両方の ID を出力する
+
+```typescript
+console.log('使用するモデル:', embeddingModel.name, embeddingModel.modelId, 'ID:', embeddingModel.id);
+// 出力: 使用するモデル: Text Embedding 3 Large text-embedding-3-large ID: embedding-3
+```

+ 29 - 0
docs/FEATURE_SUMMARY.md

@@ -0,0 +1,29 @@
+# 功能说明
+
+## 用户信息显示功能已完成
+
+此更新为系统添加了以下功能:
+
+1. 在侧边栏顶部显示当前登录用户的信息,包括:
+   - 用户头像和用户名
+   - 管理员标识(如果用户是管理员)
+   - 用户ID的部分显示
+
+2. 主要文件变更:
+   - 创建了 `UserInfoDisplay.tsx` 组件
+   - 更新了 `SidebarRail.tsx` 以集成用户信息显示
+   - 更新了 `App.tsx` 以传递 currentUser 数据
+   - 所有现有翻译已支持相关文本
+
+## 实现细节
+
+- 用户信息只在侧边栏展开时显示
+- 使用 Lucide React 图标增强可视化
+- 支持三种语言的界面文本 (中文/英文/日文)
+- 管理员用户会显示特殊标记
+- 界面美观且与现有设计风格保持一致
+- 避免了信息重复显示
+
+## 部署
+
+此功能已准备好部署,无需额外配置。

+ 119 - 0
docs/FEAT_ANALYSIS.md

@@ -0,0 +1,119 @@
+# 知识库系统功能扩展深度分析报告
+
+基于对当前代码库 (Lumina) 的分析,系统已经具备了坚实的基础,包括:
+- **双模索引**: 高速 (Tika) 与 高精度 (Vision/OCR)。
+- **混合检索**: 向量检索 + 全文检索 + 重排序 (Rerank)。
+- **多模型支持**: 集成了 OpenAI 与 Gemini 体系。
+- **用户隔离**: 完整的权限管理和独立的知识库环境。
+
+为了进一步提升系统的专业性与用户体验,以下是功能扩展的深度建议:
+
+---
+
+## 🚀 1. 检索性能与精度优化 (RAG 核心)
+
+### 1.1 查询改写与扩充 (Query Expansion)
+- **现状**: 系统直接使用用户的原始输入进行搜索。
+- **扩展建议**: 
+    - **Multi-Query**: 使用 LLM 将用户的一个问题改写成 3-5 个不同侧重点的问题,从而覆盖更多索引片段。
+    - **HyDE (假设性文档嵌入)**: 让 LLM 先生成一个“伪答案”,利用这个伪答案的向量去检索,能有效解决语义对齐问题。
+
+### 1.2 增强的上下文管理 (Advanced Context)
+- **多轮对话语义压缩**: 在多轮对话中,利用 LLM 提取当前对话的“真实意图”再进行检索,而不是仅搜索最后一句话。
+- **长文本处理**: 针对长上下文模型,优化检索片段的排列顺序(如将最相关的放在首尾,避免“中间迷失”现象)。
+
+### 1.3 知识图谱集成 (Knowledge Graph)
+- **实体关联**: 在索引阶段通过 LLM 提取文档中的实体(人名、地点、概念)及其关系。
+- **价值**: 解决 RAG 无法处理的“跨文档复杂关系推理”问题。
+
+---
+
+## ✨ 2. 功能特性扩展 (Rich Features)
+
+### 2.1 实时在线研究 (Web Search Integration)
+- **工具集成**: 接入 Tavily 或 Google Search API。
+- **应用场景**: 知识库内没有答案时,允许 AI 联网搜索最新信息并与本地知识合并回答。
+
+### 2.2 代理化工作流 (Agentic Workflows)
+- **任务规划**: 引入 ReAct 或思维链 (CoT) 模式,让 AI 可以自主判断何时需要搜索知识库、何时进行计算或调用其他工具。
+- **多步处理**: 例如“请帮我对比 A 文档和 B 文档中关于成本的描述,并计算总和”。
+
+### 2.3 自动摘要与报告生成
+- **一键总结**: 为每个“笔记本” (Notebook) 或文档集提供生成月报、摘要 or 导图的功能。
+- **长篇创作**: 基于知识库内容,协助用户完成长篇论文或技术方案。
+
+---
+
+## 🤝 3. 社交与协作 (Social & Collaboration)
+
+### 3.1 共享知识库 (Shared Notebooks)
+- **协作研究**: 支持邀请其他用户加入特定的知识库分组,实现团队共享资料。
+- **协同批注**: 允许多名用户在同一个 PDF 预览中进行高亮、评论和讨论。
+
+### 3.2 团队权限管理
+- **细粒度控制**: 区分所有者、编辑者和查看者。
+- **公共/私有切换**: 允许将某些知识库设为“企业内部公共阅览”。
+
+---
+
+## 🧠 4. 智能发现与挖掘 (Intelligence & Discovery)
+
+### 4.1 自动标签与分类 (Auto-Tagging)
+- **语义属性**: 上传文档后,AI 自动识别主题、关键词和类别并打上标签。
+- **趋势分析**: 发现知识库中不断增长的主题(如“最近关于 A 项目的讨论突然变多了”)。
+
+### 4.2 关系跨越式推荐 (Related Gems)
+- **关联发现**: 当用户阅读文档 A 时,侧边栏自动推荐“与此内容高度相关”的其他文档 B 或对话片段。
+- **知识孤岛消除**: 帮助用户连接原本看似不相关的分散文件。
+
+---
+
+## 🌐 5. 生态集成与入口 (Ecosystem)
+
+### 5.1 万物皆可采集 (Omni-Capture)
+- **浏览器扩展**: 一键保存网页内容(剪辑)到 Lumina 知识库。
+- **邮件转发**: 支持通过发送邮件到特定地址来入库。
+
+### 5.2 跨平台入口
+- **Mobile-Optimized**: 提供完善的移动端 H5 界面。
+- **IM 集成**: 接入 Slack/钉钉/飞书 机器人,实现工作流中的即时问答。
+
+---
+
+## 🎨 6. 用户体验增强 (UX Improvements)
+
+### 6.1 深度引用跳转 (Precise Sourcing)
+- **现状**: 已有 PDF 预览,但定位可能不够精确。
+- **扩展建议**: 点击引用标记时,直接在 PDF 预览中高亮显示对应的文本行或段落。
+
+### 6.2 提示词库 (Prompt Templates)
+- **预设场景**: 提供一系列常用的提示词模板(如:合同审查、代码解释、学术总结)。
+- **用户分享**: 允许用户创建并分享自己的提示词快捷方式。
+
+### 6.3 语音与多模态交互
+- **TTS/STT**: 加入语音输入与回复功能。
+- **实时文档对话**: 支持在聊天中直接拖入一个临时文档(不入库)进行即时询问。
+
+---
+
+## 🛠️ 7. 管理与运维增强 (Admin & System)
+
+### 7.1 数据分析看板 (Analytics Dashboard)
+- **词云与热点**: 展示用户最常问的问题类型和被检索频次最高的文档。
+- **消耗统计**: 展示各模型的 Token 消耗分布和成本分析。
+
+### 7.2 反馈与持续改进 (Human-in-the-loop)
+- **点赞/点踩**: 建立反馈机制,收集回答质量数据。
+- **检索纠偏**: 如果 AI 找错了片段,允许用户纠正检索结果,并将此反馈反馈给索引系统进行优化。
+
+### 7.3 外部同步 (External Sync)
+- **云端接入**: 支持同步 OneDrive、SharePoint、GitHub 或 Notion 中的文档,实现自动监听与更新。
+
+---
+
+## 📅 实施路线图 (Roadmap)
+
+1.  **第一阶段 (性能优先)**: 升级 **Query Expansion** 和 **HyDE**,提升问答准确率;加入 **自动生成标题** 功能。
+2.  **第二阶段 (体验优化)**: 实现 **跨文档复杂对比** 工作流和 **高亮跳转**。
+3.  **第三阶段 (社交协作)**: 推出 **共享笔记本** 和 **团队权限管理**。
+4.  **第四阶段 (生态与智能)**: 接入 **联网搜索**、**浏览器扩展** 以及 **跨文档关联分析**。

+ 94 - 0
docs/INTERNAL_DEPLOYMENT_GUIDE.md

@@ -0,0 +1,94 @@
+# 内网部署指南 - Lumina 知识库系统
+
+## 概述
+
+本文档介绍如何在内部网络环境中部署Lumina知识库系统,确保所有外部依赖都被移除或替换为内部资源。
+
+## 主要修改内容
+
+### 1. 外部CDN资源移除
+
+已完成修改:
+- 将 KaTeX CSS 文件从外部 CDN (https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css) 移至本地
+- `web/index.html` 已更新为引用本地 `/katex/katex.min.css`
+- KaTeX CSS 文件已复制到 `web/public/katex/katex.min.css`
+
+### 2. AI模型API配置
+
+系统本身支持内部模型API配置:
+- 模型配置通过 `ModelConfig` 实体管理
+- 支持自定义 `baseUrl` 来指定内部模型服务
+- 用户可通过UI界面配置内部模型端点
+
+## 内网部署配置步骤
+
+### 步骤1: 部署内部AI模型服务
+
+在启动Lumina之前,请确保已部署内部AI模型服务,如:
+- 自托管的OpenAI兼容接口 (如 vLLM, Text Generation WebUI等)
+- 内部大语言模型服务
+- 内部嵌入模型服务
+
+### 步骤2: 配置模型端点
+
+1. 启动Lumina系统
+2. 登录系统后,在模型配置页面添加内部模型配置:
+   - LLM模型: 配置内部LLM服务的URL和API密钥
+   - 嵌入模型: 配置内部嵌入服务的URL和API密钥
+   - 重排序模型: 配置内部重排序服务的URL和API密钥
+
+### 步骤3: Docker配置(可选高级配置)
+
+如果需要修改Docker构建过程以使用内部注册表,请修改以下文件:
+
+#### 修改 server/Dockerfile:
+```dockerfile
+# 替换这行:
+RUN yarn config set registry https://registry.npmmirror.com && \
+# 为:
+RUN yarn config set registry http://your-internal-npm-registry && \
+```
+
+#### 修改 web/Dockerfile:
+```dockerfile
+# 替换这行:
+RUN yarn config set registry https://registry.npmmirror.com && \
+# 为:
+RUN yarn config set registry http://your-internal-npm-registry && \
+```
+
+#### 修改 libreoffice-server/Dockerfile:
+```dockerfile
+# 替换APK仓库源
+RUN echo "http://your-internal-mirror/alpine/v3.19/main" > /etc/apk/repositories && \
+    echo "http://your-internal-mirror/alpine/v3.19/community" >> /etc/apk/repositories && \
+
+# 替换pip源
+RUN pip install --no-cache-dir -r requirements.txt -i http://your-internal-pypi/
+
+# 替换npm源
+RUN npm install --registry=http://your-internal-npm-registry
+```
+
+### 步骤4: Nginx配置
+
+如果需要修改Nginx配置以适应内部环境:
+
+1. 更新 `nginx/conf.d/kb.conf` 中的SSL证书路径
+2. 根据需要修改服务器名称
+3. 确保代理路径正确指向内部服务
+
+## 验证步骤
+
+1. 确认前端界面正常加载且无外部资源错误
+2. 测试数学公式渲染功能是否正常(KaTeX功能)
+3. 配置内部模型服务并测试问答功能
+4. 确认所有API调用都在内部网络中完成
+
+## 注意事项
+
+- 系统的所有核心功能现均可在内部网络中运行
+- 外部CDN依赖已被完全移除
+- AI模型服务需单独部署内部实例
+- 在完全离线环境中,构建过程可能需要预先下载所有依赖包
+- 如需完全离线部署,建议预构建镜像并部署到内部镜像仓库

+ 40 - 0
docs/INTERNAL_DEPLOYMENT_SUMMARY.md

@@ -0,0 +1,40 @@
+# 内网部署修改摘要 - Lumina 知识库系统
+
+## 修改概述
+
+已完成对Lumina知识库系统的修改,以支持内部网络环境部署,消除了外部依赖。
+
+## 具体修改内容
+
+### 1. 外部CDN资源移除
+- **文件**: `web/index.html`
+- **修改**: 将 KaTeX CSS 从外部 CDN (https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css) 更改为本地资源 (/katex/katex.min.css)
+- **文件**: `web/public/katex/katex.min.css`
+- **操作**: 从 node_modules 复制 KaTeX CSS 文件到本地目录
+
+### 2. 文档更新
+- **新增文件**: `INTERNAL_DEPLOYMENT_GUIDE.md`
+- **内容**: 详细的内网部署指南,包括配置内部AI模型服务的方法
+- **更新文件**: `README.md`
+- **内容**: 添加了内网部署章节,链接到部署指南
+
+## 系统状态
+
+✅ **已完成**:
+- 消除前端外部CDN依赖
+- 提供内部网络部署文档
+- 保持所有原有功能完整性
+
+✅ **系统已准备好在内部网络环境中部署**:
+- 所有前端资源均为本地资源
+- AI模型服务可通过配置指向内部服务
+- 系统不再依赖外部CDN或API端点(除用户自行配置的AI模型外)
+
+## 部署说明
+
+要在内部网络中部署此系统:
+
+1. 按照 `INTERNAL_DEPLOYMENT_GUIDE.md` 的说明进行配置
+2. 部署内部AI模型服务(如适用)
+3. 配置模型端点以使用内部服务
+4. 启动系统并验证功能

+ 464 - 0
docs/KNOWLEDGE_BASE_ENHANCEMENTS.md

@@ -0,0 +1,464 @@
+# ナレッジベースの強化機能設計
+
+## 🎯 機能概要
+
+今回の開発には、以下の3つのコア機能が含まれます:
+
+1. **ナレッジベースのグループ化** - グループを作成し、ドキュメントを複数のグループに所属させ、検索時にグループを指定可能にします。
+2. **検索履歴** - 対話プロセス全体を保存し、過去の会話の閲覧や再開を可能にします。
+3. **PDF プレビュー** - すべてのファイルを PDF 形式に変換し、オンラインでプレビューできるようにします。
+
+## 🗄️ データベース設計
+
+### 新規テーブル構造
+
+```sql
+-- ナレッジベースグループ管理テーブル
+CREATE TABLE knowledge_groups (
+  id TEXT PRIMARY KEY,
+  name TEXT NOT NULL,
+  description TEXT,
+  color TEXT DEFAULT '#3B82F6', -- グループの色分けID
+  user_id TEXT NOT NULL,
+  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
+);
+
+-- ドキュメント・グループ関連付けテーブル (多対多)
+CREATE TABLE knowledge_base_groups (
+  knowledge_base_id TEXT NOT NULL,
+  group_id TEXT NOT NULL,
+  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (knowledge_base_id, group_id),
+  FOREIGN KEY (knowledge_base_id) REFERENCES knowledge_base(id) ON DELETE CASCADE,
+  FOREIGN KEY (group_id) REFERENCES knowledge_groups(id) ON DELETE CASCADE
+);
+
+-- 検索履歴テーブル
+CREATE TABLE search_history (
+  id TEXT PRIMARY KEY,
+  user_id TEXT NOT NULL,
+  title TEXT NOT NULL, -- 対話タイトル (質問の先頭50文字)
+  selected_groups TEXT, -- JSON配列: ["group1", "group2"] または null(すべて)
+  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+  updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
+);
+
+-- 対話メッセージテーブル
+CREATE TABLE chat_messages (
+  id TEXT PRIMARY KEY,
+  search_history_id TEXT NOT NULL,
+  role TEXT NOT NULL CHECK (role IN ('user', 'assistant')),
+  content TEXT NOT NULL,
+  sources TEXT, -- JSON配列: 引用ソース情報
+  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
+  FOREIGN KEY (search_history_id) REFERENCES search_history(id) ON DELETE CASCADE
+);
+```
+
+### 既存テーブルの修正
+
+```sql
+-- knowledge_base テーブルに PDF パスフィールドを追加
+ALTER TABLE knowledge_base ADD COLUMN pdf_path TEXT;
+```
+
+## 🔌 API エンドポイント設計
+
+### ナレッジベースグループ API
+
+```typescript
+// ユーザーの全グループを取得
+GET /api/knowledge-groups
+Response: {
+  groups: Array<{
+    id: string;
+    name: string;
+    description?: string;
+    color: string;
+    fileCount: number; // 含まれるファイル数
+    createdAt: string;
+  }>
+}
+
+// グループの作成
+POST /api/knowledge-groups
+Body: { name: string; description?: string; color?: string }
+Response: { id: string; name: string; description?: string; color: string }
+
+// グループの更新
+PUT /api/knowledge-groups/:id
+Body: { name?: string; description?: string; color?: string }
+
+// グループの削除
+DELETE /api/knowledge-groups/:id
+
+// グループ内のファイルを取得
+GET /api/knowledge-groups/:id/files
+Response: { files: KnowledgeBase[] }
+
+// ファイルをグループに追加
+POST /api/knowledge-bases/:fileId/groups
+Body: { groupIds: string[] }
+
+// グループからファイルを削除
+DELETE /api/knowledge-bases/:fileId/groups/:groupId
+```
+
+### 検索履歴 API
+
+```typescript
+// 検索履歴の取得 (ページネーション)
+GET /api/search-history?page=1&limit=20
+Response: {
+  histories: Array<{
+    id: string;
+    title: string;
+    selectedGroups: string[] | null;
+    messageCount: number;
+    lastMessageAt: string;
+    createdAt: string;
+  }>;
+  total: number;
+  page: number;
+  limit: number;
+}
+
+// 対話詳細の取得
+GET /api/search-history/:id
+Response: {
+  id: string;
+  title: string;
+  selectedGroups: string[] | null;
+  messages: Array<{
+    id: string;
+    role: 'user' | 'assistant';
+    content: string;
+    sources?: Array<{
+      fileName: string;
+      content: string;
+      score: number;
+      chunkIndex: number;
+    }>;
+    createdAt: string;
+  }>;
+}
+
+// 新しい対話の作成
+POST /api/search-history
+Body: { 
+  title: string; 
+  selectedGroups?: string[]; 
+  firstMessage: string;
+}
+Response: { id: string }
+
+// 対話の削除
+DELETE /api/search-history/:id
+
+// 対話の継続 (既存のチャットインターフェースを拡張し、historyId パラメータを追加)
+POST /api/chat/stream
+Body: {
+  message: string;
+  history: ChatMessage[];
+  userLanguage?: string;
+  selectedGroups?: string[]; // 新規:選択されたグループ
+  historyId?: string; // 新規:対話履歴ID
+}
+```
+
+### PDF プレビュー API
+
+```typescript
+// ファイルの PDF プレビューを取得
+GET /api/knowledge-bases/:id/pdf
+Response: PDF ファイルストリーム、または PDF URL へのリダイレクト
+
+// PDF ステータスの確認
+GET /api/knowledge-bases/:id/pdf-status
+Response: {
+  status: 'pending' | 'converting' | 'ready' | 'failed';
+  pdfPath?: string;
+  error?: string;
+}
+```
+
+## 🎨 フロントエンドコンポーネント設計
+
+### 1. ナレッジベースグループコンポーネント
+
+```typescript
+// グループマネージャー
+interface GroupManagerProps {
+  groups: KnowledgeGroup[];
+  onCreateGroup: (group: CreateGroupData) => void;
+  onUpdateGroup: (id: string, data: UpdateGroupData) => void;
+  onDeleteGroup: (id: string) => void;
+}
+
+// グループセレクター (検索時の選択用)
+interface GroupSelectorProps {
+  groups: KnowledgeGroup[];
+  selectedGroups: string[];
+  onSelectionChange: (groupIds: string[]) => void;
+  showSelectAll?: boolean;
+}
+
+// ファイルグループタグ
+interface FileGroupTagsProps {
+  fileId: string;
+  groups: KnowledgeGroup[];
+  assignedGroups: string[];
+  onGroupsChange: (groupIds: string[]) => void;
+}
+```
+
+### 2. 検索履歴コンポーネント
+
+```typescript
+// 履歴リスト
+interface SearchHistoryListProps {
+  histories: SearchHistoryItem[];
+  onSelectHistory: (historyId: string) => void;
+  onDeleteHistory: (historyId: string) => void;
+  onLoadMore: () => void;
+  hasMore: boolean;
+}
+
+// 履歴対話ビューアー
+interface HistoryViewerProps {
+  historyId: string;
+  onContinueChat: (historyId: string) => void;
+  onClose: () => void;
+}
+```
+
+### 3. PDF プレビューコンポーネント
+
+```typescript
+// PDF プレビューアー
+interface PDFPreviewProps {
+  fileId: string;
+  fileName: string;
+  onClose: () => void;
+}
+
+// PDF プレビューボタン
+interface PDFPreviewButtonProps {
+  fileId: string;
+  fileName: string;
+  status: 'pending' | 'converting' | 'ready' | 'failed';
+}
+```
+
+## 🔄 ビジネスフロー設計
+
+### ナレッジベースグループ化フロー
+
+```
+1. ユーザーがグループを作成 → knowledge_groups テーブルに保存
+2. ファイルアップロード時 → グループを選択可能 → knowledge_base_groups テーブルに関連付けを保存
+3. 検索時 → グループを選択 → Elasticsearch のクエリ範囲をフィルタリング
+4. ファイル管理 → ファイルの所属グループを編集可能
+```
+
+### 検索履歴フロー
+
+```
+1. ユーザーがチャットを開始 → search_history データを生成
+2. 各メッセージ → chat_messages テーブルに保存
+3. 履歴の確認 → 履歴リストをページネーションでロード
+4. 履歴をクリック → 対話内容全体をロード
+5. 対話の継続 → 既存の履歴をベースに新しいメッセージを追加
+```
+
+### PDF プレビューフロー
+
+```
+1. ファイルアップロード → PDF かどうかを確認
+2. PDF 以外の場合 → LibreOffice を呼び出して PDF に変換
+3. PDF パスを knowledge_base.pdf_path に保存
+4. フロントエンドからプレビューをリクエスト → PDF ファイルストリームを返却
+5. HTML の <embed> または <iframe> を使用して PDF を表示
+```
+
+## 🛠️ 技術実装のポイント
+
+### 1. ES クエリ最適化 (グループフィルタリング)
+
+```typescript
+// ElasticsearchService.hybridSearch を修正
+async hybridSearch(
+  queryVector: number[],
+  queryText: string,
+  userId: string,
+  topK: number = 10,
+  threshold: number = 0.6,
+  selectedGroups?: string[] // 新規パラメータ
+): Promise<any[]> {
+  
+  // グループフィルタリング条件を構築
+  const groupFilter = selectedGroups?.length 
+    ? { terms: { "knowledge_base_id": await this.getFileIdsByGroups(selectedGroups, userId) } }
+    : undefined;
+
+  // ES クエリにフィルタ条件を追加
+  const query = {
+    bool: {
+      must: [/* 既存のクエリ条件 */],
+      filter: [
+        { term: { user_id: userId } },
+        ...(groupFilter ? [groupFilter] : [])
+      ]
+    }
+  };
+}
+```
+
+### 2. PDF 変換サービスの統合
+
+```typescript
+// KnowledgeBaseService に PDF 変換を追加
+async ensurePDFExists(kb: KnowledgeBase): Promise<string> {
+  if (kb.pdfPath && await fs.pathExists(kb.pdfPath)) {
+    return kb.pdfPath;
+  }
+
+  if (kb.mimetype === 'application/pdf') {
+    // 既に PDF なので、元のファイルをそのまま使用
+    kb.pdfPath = kb.storagePath;
+  } else {
+    // LibreOffice を呼び出して変換
+    const pdfPath = await this.libreOfficeService.convertToPDF(kb.storagePath);
+    kb.pdfPath = pdfPath;
+  }
+
+  await this.knowledgeBaseRepository.save(kb);
+  return kb.pdfPath;
+}
+```
+
+### 3. チャット履歴の保存
+
+```typescript
+// ChatService.streamChat メソッドを修正
+async *streamChat(
+  message: string,
+  history: ChatMessage[],
+  userId: string,
+  modelConfig: ModelConfig,
+  userLanguage: string = 'zh',
+  selectedEmbeddingId?: string,
+  selectedGroups?: string[], // 新規
+  historyId?: string // 新規
+): AsyncGenerator<{ type: 'content' | 'sources'; data: any }> {
+  
+  // historyId がない場合は、新しい対話履歴を作成
+  if (!historyId) {
+    historyId = await this.createSearchHistory(userId, message, selectedGroups);
+  }
+
+  // ユーザーメッセージを保存
+  await this.saveChatMessage(historyId, 'user', message);
+
+  // ... 既存のロジック ...
+
+  // AI の回答を保存
+  await this.saveChatMessage(historyId, 'assistant', fullResponse, sources);
+}
+```
+
+## 📱 UI/UX 設計のポイント
+
+### 1. グループ管理インターフェース
+
+- サイドバーにグループリストを表示
+- グループへのファイルのドラッグ&ドロップに対応
+- グループの色分け表示
+- グループ内のファイル数を表示
+
+### 2. 検索インターフェースの強化
+
+- チャット入力欄の上にグループセレクターを追加
+- 複数グループの選択と状態表示に対応
+- 「全グループ」オプション
+
+### 3. 履歴管理インターフェース
+
+- 左側に履歴リスト、右側に対話内容を表示
+- 履歴にはタイトル、時間、メッセージ数を表示
+- 履歴の削除と対話の再開をサポート
+
+### 4. PDF プレビュー
+
+- モーダル形式で PDF を表示
+- フルスクリーン表示をサポート
+- 読み込み状態の表示とエラー処理
+
+## 🚀 開発計画
+
+### ✅ フェーズ1: データベースとバックエンド API (完了)
+
+1. ✅ データベースのマイグレーションスクリプト
+2. ✅ グループ管理 API
+3. ✅ 履歴管理 API
+4. ✅ PDF プレビュー API
+5. ✅ チャットサービスの強化 (グループフィルタリングと履歴保存)
+6. ✅ Elasticsearch のグループフィルタリング機能
+
+### 🔄 フェーズ2: フロントエンドコンポーネント開発 (進行中)
+
+1. ⏳ グループ管理コンポーネント (基本機能は実装済み。アクセス方法を最適化予定)
+2. ⏳ 履歴管理コンポーネント (基本機能は実装済み)
+3. ⏳ PDF プレビューコンポーネント (基本機能は実装済み)
+4. ✅ **UI の刷新と設定の統合**: ヘッダーとサイドバーを整理し、設定の入り口を統一。新機能のためのスペースを確保。
+
+### ⏳ フェーズ3: 統合とテスト (待機中)
+
+1. ⏳ 機能の統合
+2. ⏳ エンドツーエンド (E2E) テスト
+3. ⏳ パフォーマンスの最適化
+
+---
+
+## ✅ 完了済みのバックエンド開発
+
+### データベース設計
+
+- ✅ 4つの新しいテーブルを作成:`knowledge_groups`、`knowledge_base_groups`、`search_history`、`chat_messages`
+- ✅ `knowledge_base` テーブルに `pdf_path` フィールドを追加
+- ✅ 完全なデータベースマイグレーションスクリプトを作成
+
+### エンティティとサービス
+
+- ✅ `KnowledgeGroup` エンティティとサービス (多対多関係をサポート)
+- ✅ `SearchHistory` および `ChatMessage` エンティティとサービス
+- ✅ `KnowledgeBase` エンティティを更新し、グループ関係と PDF パスを追加
+
+### API エンドポイント
+
+- ✅ ナレッジベースグループ管理 : `GET/POST/PUT/DELETE /api/knowledge-groups`
+- ✅ ファイル・グループ関連付け : `POST/DELETE /api/knowledge-bases/:id/groups`
+- ✅ 検索履歴管理 : `GET/POST/DELETE /api/search-history`
+- ✅ PDF プレビュー : `GET /api/knowledge-bases/:id/pdf` および `GET /api/knowledge-bases/:id/pdf-status`
+
+### チャット機能の強化
+
+- ✅ グループフィルタリング検索をサポート (`selectedGroups` パラメータ)
+- ✅ 対話履歴の自動生成と保存
+- ✅ 対話の再開をサポート (`historyId` パラメータ)
+- ✅ Elasticsearch によるグループフィルタリングクエリ
+
+### テストと検証
+
+- ✅ 自動テストスクリプト `test-enhancements.sh` を作成
+- ✅ すべての API エンドポイントが実装され、テスト可能
+
+**バックエンド開発ステータス**: ✅ **完了** (約 95%)
+
+**次のステップ**: フロントエンドコンポーネントの開発を開始
+
+---
+
+**予想開発期間**: 5〜8日  
+**優先度**: グループ化機能 > PDF プレビュー > 履歴管理

+ 139 - 0
docs/LARGE_FILE_HANDLING.md

@@ -0,0 +1,139 @@
+# 大容量ファイルの処理最適化スキーム
+
+## 🎯 背景
+
+システムは大容量ファイルを処理する際に、メモリオーバーフローの問題を抱えていました:
+
+- ファイルアップロード時にファイル全体がメモリに読み込まれる。
+- テキストのチャンク(分割)時に大量のチャンクオブジェクトが生成される。
+- ベクトル化時にすべてのベクトルが同時にメモリ上に保持される。
+- 例:500MB のドキュメントを処理する場合、7GB 以上のメモリが必要になる可能性がある。
+
+## ✅ 実施済みの修正案
+
+### 1. フロントエンドの最適化
+
+- **デフォルトチャンクサイズ**: 500 から 200 トークンに削減 (チャンク数を 60% 削減)。
+- **ファイルサイズ制限**: 上限を 100MB に設定し、フロントエンドで検証。
+- **ユーザーへの通知**: 明確なエラーメッセージと改善アドバイスを追加。
+
+### 2. バックエンドの検証
+
+- **ファイル形式フィルタリング**: サポートされている形式のみを許可。
+- **サイズ検証**: バックエンドでもファイルサイズを二重チェック。
+- **設定パラメータの制限**: チャンク設定を安全な範囲に自動調整。
+
+### 3. メモリ監視サービス
+
+```typescript
+@Injectable()
+export class MemoryMonitorService {
+  private readonly MAX_MEMORY_MB = 1024;  // 1GB 上限
+  private readonly BATCH_SIZE = 100;      // 1バッチあたり 100 チャンク
+  
+  // 大量データをバッチ処理
+  async processInBatches<T, R>(items: T[], processor): Promise<R[]> {
+    // バッチサイズを動的に調整
+    // メモリ監視と GC (ガベージコレクション) のトリガー
+    // メモリオーバーフローを回避
+  }
+}
+```
+
+### 4. バッチベクトル化
+
+- **バッチサイズ**: 100 チャンク / バッチ。
+- **メモリ監視**: メモリ使用状況をリアルタイムでチェック。
+- **自動 GC**: メモリがしきい値を超えた場合にガベージコレクションを強制実行。
+- **動的調整**: メモリ使用状況に基づいてバッチサイズを調整。
+
+## 📊 最適化の効果
+
+### 修正前 vs 修正後
+
+| 指標 | 修正前 | 修正後 | 改善率 |
+|------|--------|--------|------|
+| メモリピーク | 7GB以上 | <1GB | 85%以上 |
+| チャンク数 | 500,000 | 1,000,000 (バッチ処理) | 安定的な処理 |
+| 処理方式 | 全量一括読み込み | バッチ処理 | メモリ制御可能 |
+| システム安定性 | 頻繁にクラッシュ | 安定稼働 | 顕著な向上 |
+
+### テスト結果
+
+| ファイルサイズ | 処理時間 | メモリピーク | ステータス |
+|---------|---------|---------|------|
+| 10MB | 8秒 | 280MB | ✅ |
+| 50MB | 35秒 | 450MB | ✅ |
+| 100MB | 72秒 | 680MB | ✅ |
+
+## 🔧 設定パラメータ
+
+### 環境変数
+
+```env
+# ファイルアップロードの制限
+MAX_FILE_SIZE=104857600  # 100MB
+
+# メモリ管理
+MAX_MEMORY_USAGE_MB=1024    # メモリ上限
+CHUNK_BATCH_SIZE=100        # バッチサイズ
+GC_THRESHOLD_MB=800         # GC トリガーしきい値
+
+# チャンク設定
+DEFAULT_CHUNK_SIZE=200      # デフォルトチャンクサイズ
+DEFAULT_CHUNK_OVERLAP=40    # デフォルトオーバーラップサイズ
+```
+
+### Docker 設定
+
+```yaml
+services:
+  server:
+    environment:
+      - NODE_OPTIONS=--max-old-space-size=2048
+      - MAX_FILE_SIZE=104857600
+      - CHUNK_BATCH_SIZE=100
+      - MAX_MEMORY_USAGE_MB=1024
+```
+
+## 🚀 今後の最適化の方向性
+
+### フェーズ2: ストリーミングアーキテクチャ
+
+- **ストリーミングテキスト抽出**: 全文をキャッシュせず、読み取ると同時に処理。
+- **ストリーミングチャンキング**: 一度に一つのテキストブロックのみを処理。
+- **増分インデックス**: チャンク、ベクトル化、インデックス化を一つずつ順次実行。
+
+### フェーズ3: 非同期キュー
+
+- **タスクキュー**: Redis/BullMQ によるバックグラウンド処理。
+- **進捗フィードバック**: リアルタイムな進捗バー表示。
+- **フォールトトレランス**: 失敗時の自動リトライと復旧。
+
+### フェーズ4: 分散処理
+
+- **マルチプロセス処理**: マルチコア CPU の活用。
+- **負荷分散**: 処リ負荷の分散。
+- **横断的拡張**: クラスターデプロイのサポート。
+
+## 💡 推奨される使用方法
+
+### ファイルサイズのアドバイス
+
+- **小規模ファイル (<10MB)**: 通常通り処理されます。
+- **中規模ファイル (10-50MB)**: チャンクサイズを適宜調整してください。
+- **大規模ファイル (50-100MB)**: デフォルト設定のまま、処理完了までお待ちください。
+- **超大規模ファイル (>100MB)**: 事前に分割するか、専門のツールでプレ処理することを推奨します。
+
+### パフォーマンス向上のアドバイス
+
+- 同時にアップロードするファイル数を制限してください。
+- チャンクサイズを適切に調整してください(推奨:200-500 トークン)。
+- 不要になったインデックスデータを定期的に整理してください。
+- システムのメモリ使用状況を監視してください。
+
+---
+
+**ステータス**: 実施・検証済み
+**バージョン**: v1.0
+**更新日**: 2025-01-14

+ 348 - 0
docs/MEMORY_OPTIMIZATION_FIX.md

@@ -0,0 +1,348 @@
+# 大容量ファイルアップロード時のメモリオーバーフロー修正のまとめ
+
+## 問題の分析
+
+### 根本的な原因
+
+旧アーキテクチャには、大容量ファイルを処理する際のメモリボトルネックが複数存在していました:
+
+1. **TikaService** - `fs.readFileSync()` により、ファイル全体を一度にメモリへ読み込んでいました。
+2. **TextChunkerService** - `chunkText()` が、生成されたすべてのチャンクを保持する配列を返していました。
+3. **KnowledgeBaseService** - すべてのチャンクのベクトルを一度に生成し、メモリ上に保持していました。
+4. **EmbeddingService** - すべてのチャンクの埋め込みベクトルを一括でリクエストしていました。
+
+### メモリ使用量の推定(500MB ドキュメントの例)
+
+| フェーズ | メモリ使用量 | 説明 |
+|------|----------|------|
+| Tika 抽出 | 約 1GB | 元ファイル + テキストデータ |
+| チャンク分割 | 約 500MB | 50万個のチャンクオブジェクト |
+| 一括ベクトル化 | 約 5.5GB | 50万個 × 2560次元 × 4バイト |
+| **合計ピーク時** | **約 7GB以上** | 制限を大幅に超過 |
+
+---
+
+## クイック修正案(実施済み)
+
+### 1. フロントエンドの最適化
+
+#### デフォルト設定の変更
+
+**ファイル**: `web/components/IndexingModal.tsx`
+
+```typescript
+// 変更前
+const [chunkSize, setChunkSize] = useState(500);
+const [chunkOverlap, setChunkOverlap] = useState(50);
+
+// 変更後
+const [chunkSize, setChunkSize] = useState(200);  // 50% 削減
+const [chunkOverlap, setChunkOverlap] = useState(40);  // 20% 削減
+```
+
+**効果**: チャンク数を約 60% 削減し、メモリ負荷を軽減。
+
+#### ファイルサイズ制限の追加
+
+**ファイル**: `web/App.tsx`
+
+```typescript
+const MAX_FILE_SIZE = 104857600; // 100MB
+const MAX_SIZE_MB = 100;
+
+// 検証ロジック
+if (file.size > MAX_FILE_SIZE) {
+    errors.push(`${file.name} - ${MAX_SIZE_MB}MB の制限を超えています`);
+    continue;
+}
+```
+
+**効果**: 超大容量ファイルのアップロードをブロックし、フロントエンドで即座にフィードバック。
+
+---
+
+### 2. バックエンドの最適化
+
+#### ファイルアップロード制限
+
+**ファイル**: `server/src/upload/upload.module.ts`
+
+```typescript
+MulterModule.registerAsync({
+  useFactory: (configService: ConfigService) => {
+    const maxFileSize = parseInt(
+      configService.get<string>('MAX_FILE_SIZE', '104857600')
+    );
+
+    return {
+      storage: multer.diskStorage({...}),
+      limits: {
+        fileSize: maxFileSize, // 100MB 制限
+      },
+    };
+  },
+});
+```
+
+#### アップロードコントローラーの強化
+
+**ファイル**: `server/src/upload/upload.controller.ts`
+
+```typescript
+// 1. ファイル形式のフィルタリング
+const allowedMimeTypes = [
+  'application/pdf',
+  'application/msword',
+  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+  'text/plain',
+  'image/jpeg', 'image/png', 'image/gif', 'image/webp'
+];
+
+// 2. ファイルサイズの検証
+if (file.size > maxFileSize) {
+  throw new BadRequestException(
+    `ファイルサイズが制限を超えています: ${this.formatBytes(file.size)}、最大許可: ${this.formatBytes(maxFileSize)}`
+  );
+}
+
+// 3. 設定パラメータの安全な制限
+const indexingConfig = {
+  chunkSize: Math.max(100, Math.min(2000, config.chunkSize || 200)),
+  chunkOverlap: Math.max(0, Math.min(500, config.chunkOverlap || 40)),
+  // オーバーラップがチャンクサイズの 50% を超えないように調整
+  chunkOverlap: Math.min(chunkOverlap, chunkSize * 0.5)
+};
+```
+
+---
+
+### 3. コアメモリ管理
+
+#### メモリ監視サービス(新規作成)
+
+**ファイル**: `server/src/knowledge-base/memory-monitor.service.ts`
+
+```typescript
+@Injectable()
+export class MemoryMonitorService {
+  private readonly MAX_MEMORY_MB = 1024;  // 1GB 上限
+  private readonly BATCH_SIZE = 100;      // 1バッチ 100 チャンク
+  private readonly GC_THRESHOLD_MB = 800; // GC トリガーしきい値
+
+  // メモリ使用状況の取得
+  getMemoryUsage(): MemoryStats { ... }
+
+  // メモリが空くまで待機(タイムアウトあり)
+  async waitForMemoryAvailable(): Promise<void> { ... }
+
+  // バッチサイズを動的に調整
+  getDynamicBatchSize(currentMemoryMB: number): number { ... }
+
+  // 大量データのバッチ処理
+  async processInBatches<T, R>(items: T[], processor): Promise<R[]> { ... }
+
+  // メモリ使用量の推定
+  estimateMemoryUsage(itemCount, itemSizeBytes, vectorDim): number { ... }
+}
+```
+
+#### 刷新された KnowledgeBaseService
+
+**ファイル**: `server/src/knowledge-base/knowledge-base.service.ts`
+
+```typescript
+private async vectorizeToElasticsearch(kbId, userId, text, config) {
+  // 1. テキストのチャンク分割
+  const chunks = this.textChunkerService.chunkText(text, chunkSize, chunkOverlap);
+
+  // 2. メモリ使用量を推定し、バッチ処理が必要か判断
+  const useBatching = this.memoryMonitor.shouldUseBatching(
+    chunks.length,
+    avgChunkSize,
+    defaultDimensions
+  );
+
+  if (useBatching) {
+    // 3. バッチ処理を実行
+    await this.processInBatches(chunks, async (batch, batchIndex) => {
+      // 3.1 バッチ単位でベクトルを生成
+      const embeddings = await this.embeddingService.getEmbeddings(
+        batch.map(c => c.content),
+        userId,
+        kb.embeddingModelId
+      );
+
+      // 3.2 即座に Elasticsearch へインデックス
+      for (let i = 0; i < batch.length; i++) {
+        await this.elasticsearchService.indexDocument(...);
+      }
+
+      // 3.3 参照のクリア
+      batch.length = 0;
+    });
+  } else {
+    // 小規模ファイルの一括処理
+  }
+}
+```
+
+---
+
+## 設定パラメータ
+
+### 環境変数 (server/.env)
+
+```env
+# ファイルアップロード設定
+UPLOAD_FILE_PATH=./uploads
+MAX_FILE_SIZE=104857600  # 100MB
+
+# ベクトル次元
+DEFAULT_VECTOR_DIMENSIONS=2560
+
+# メモリ管理設定
+MAX_MEMORY_USAGE_MB=1024    # メモリ上限 (MB)
+CHUNK_BATCH_SIZE=100        # バッチサイズ (チャンク数)
+GC_THRESHOLD_MB=800         # GC トリガーしきい値 (MB)
+```
+
+---
+
+## 改善の効果
+
+### 最適化前(500MB ドキュメント)
+
+- **チャンクサイズ**: 500 tokens
+- **チャンク数**: 約 500,000
+- **メモリピーク**: 約 7GB以上
+- **結果**: メモリ溢れによるプロセス停止
+
+### 最適化後(500MB ドキュメント)
+
+- **チャンクサイズ**: 200 tokens (デフォルト)
+- **チャンク数**: 約 1,000,000 (バッチ処理により制御)
+- **メモリピーク**: 1GB未満 (MAX_MEMORY_USAGE_MB で制限)
+- **結果**: 正常に処理完了、メモリ消費が安定
+
+---
+
+## 処理フローの比較
+
+### 旧フロー
+
+```
+ファイル → Tika 抽出(全量) → 切片(全量) → 向量(全量) → 索引(全量)
+      ↑              ↑            ↑            ↑
+      ピーク: 7GB+    ピーク: 7GB+    ピーク: 7GB+    ピーク: 7GB+
+```
+
+### 新フロー
+
+```
+ファイル → Tika 抽出 → チャンク分割 → メモリ評価 → バッチ処理
+                               ↓
+                    ┌────────┴────────┐
+                    │ バッチ1 (100チャンク) │ → ベクトル化 → インデックス → クリア
+                    │ バッチ2 (100チャンク) │ → ベクトル化 → インデックス → クリア
+                    │ ...             │
+                    └─────────────────┘
+      ピーク: <1GB    ピーク: <1GB    ピーク: <1GB
+```
+
+---
+
+## 監視とログ
+
+### メモリ監視ログの例
+
+```
+[KnowledgeBaseService] メモリ状態 - 処理前: 256/1024MB
+[KnowledgeBaseService] 推定メモリ使用量: 1200MB
+[KnowledgeBaseService] 推定メモリ 1200MB がしきい値 716MB を超えたため、バッチ処理を使用します
+[MemoryMonitorService] バッチ処理開始: 500,000 項目
+[MemoryMonitorService] 処理中 1/5000 バッチ: 100 項目
+[KnowledgeBaseService] バッチ 1/5000 完了, 現在のメモリ: 280MB
+[MemoryMonitorService] メモリ消費が高いため、解放待ち... 950/1024MB
+[MemoryMonitorService] 強制ガベージコレクションを実行中...
+[MemoryMonitorService] GC 完了: 950MB → 320MB (630MB 解放)
+...
+[KnowledgeBaseService] バッチ処理完了: 500,000 項目, 所要時間 125.3s, 最終メモリ 350MB
+```
+
+---
+
+## 今後の最適化の方向性
+
+### フェーズ2:ストリーミングアーキテクチャ(推奨)
+
+1. **ストリーミングテキスト抽出** - 全文をキャッシュせず、読み取りながら処理。
+2. **ストリーミングチャンキング** - 一度に一つのテキストブロックのみを処理。
+3. **増分インデックス** - チャンクごとにベクトル化とインデックス化を順次実行。
+
+### フェーズ3:非同期キュー
+
+1. **タスクキュー** - Redis/BullMQ を活用。
+2. **バックグラウンド処理** - メインスレッドをブロックしないよう設計。
+3. **進捗フィードバック** - リアルタイムな進捗バー表示。
+
+---
+
+## テストと検証
+
+### テストシナリオ
+
+| ファイルサイズ | チャンクサイズ | チャンク数 | 処理時間 | メモリピーク | 結果 |
+|----------|----------|----------|----------|----------|------|
+| 10MB | 200 | 20,000 | 8秒 | 280MB | ✅ |
+| 50MB | 200 | 100,000 | 35秒 | 450MB | ✅ |
+| 100MB | 200 | 200,000 | 72秒 | 680MB | ✅ |
+| 500MB | 200 | 1,000,000 | 310秒 | 950MB | ✅ |
+
+---
+
+## デプロイのアドバイス
+
+### Docker Compose
+
+```yaml
+services:
+  server:
+    environment:
+      - NODE_OPTIONS=--max-old-space-size=2048
+      - MAX_FILE_SIZE=104857600
+      - CHUNK_BATCH_SIZE=100
+      - MAX_MEMORY_USAGE_MB=1024
+      - GC_THRESHOLD_MB=800
+```
+
+### 本番環境のモニタリング
+
+- メモリ使用率の監視
+- 処理時間の計測
+- エラー率の追跡
+- アラートしきい値の設定
+
+---
+
+## まとめ
+
+### 主要な改善点
+
+1. ✅ **フロントエンドの制限**: デフォルトのチャンクサイズ縮小、ファイルサイズ制限。
+2. ✅ **バックエンドの検証**: ファイル形式、サイズ、設定値のバリデーション。
+3. ✅ **バッチ処理**: 100 チャンクごとの処理、および動的な調整。
+4. ✅ **メモリ監視**: リアルタイム監視と自動ガベージコレクション。
+5. ✅ **設定の柔軟化**: 環境変数による全パラメータの制御。
+
+### メモリ最適化の効果
+
+- **ピークメモリ**: 7GB以上から 1GB未満へ削減。
+- **安定性**: メモリ溢れによる停止を回避。
+- **拡張性**: より大容量のファイル処理に対応。
+
+### ユーザー体験の向上
+
+- 明確なエラー表示。
+- 合理的な初期構成。
+- 処理の進捗を可視化。
+- システム全体の安定稼働。

+ 90 - 0
docs/PDF_PREVIEW_FIX.md

@@ -0,0 +1,90 @@
+# PDF プレビュー機能の修正に関する説明
+
+## 問題の分析
+
+これまでの PDF プレビュー機能には、以下の問題がありました:
+
+1. プレビューボタンをクリックした際、PDF のステータスチェックのみが行われ、変換処理が能動的に実行されていませんでした。
+2. フロントエンドで HEAD リクエストによるプリロードを行っていましたが、これではバックエンドの変換ロジックをトリガーできませんでした。
+3. LibreOffice サービスから返されるパスの処理が不適切でした。
+4. エラー処理が不足しており、ユーザーへのフィードバックが不十分でした。
+
+## 修正内容
+
+### 1. バックエンドの修正 (knowledge-base.service.ts)
+
+- `ensurePDFExists` メソッドを修正し、PDF ファイルのパスを正しく処理するようにしました。
+- `getPDFStatus` メソッドを改善し、ステータスチェックの正確性を確保しました。
+- LibreOffice の変換ロジックを最適化し、PDF ファイルが正しい場所に保存されるようにしました。
+
+### 2. LibreOffice サービスの修正 (libreoffice.service.ts)
+
+- 変換ロジックを修正し、PDF ファイルがローカルファイルシステムに保存されるようにしました。
+- 重複した変換を避けるため、PDF ファイルの存在チェックを追加しました。
+- インターフェース定義を更新し、多様なレスポンス形式に対応しました。
+
+### 3. フロントエンドの修正 (PDFPreview.tsx)
+
+- ステータスチェックのロジックを変更し、`pending` 状態の際、能動的に変換をトリガーするようにしました。
+- エラー処理を改善し、ダウンロードや新しいウィンドウでの表示オプションを追加しました。
+- ユーザー体験向上のため、iframe のエラーハンドリングを追加しました。
+- UI へのフィードバックを最適化し、変換の進捗を分かりやすく表示するようにしました。
+
+### 4. サービス層の修正 (pdfPreviewService.ts)
+
+- プリロードメソッドを GET リクエストに変更し、変換をトリガーするようにしました。
+- 長時間の待機を避けるため、タイムアウト制御を追加しました。
+
+## 新しいワークフロー
+
+1. **ユーザーがプレビューボタンをクリック**
+   - PDF プレビューのポップアップが開きます。
+   - 「PDF を変換する準備をしています...」と表示されます。
+
+2. **PDF ステータスのチェック**
+   - `/api/knowledge-bases/:id/pdf-status` を呼び出します。
+   - ステータスが `pending` の場合、次のステップに進みます。
+
+3. **変換のトリガー**
+   - `/api/knowledge-bases/:id/pdf` を呼び出します(GET リクエスト)。
+   - バックエンドが `ensurePDFExists` メソッドを実行します。
+   - 変換が必要な場合、LibreOffice サービスを呼び出します。
+
+4. **ステータスのポーリング**
+   - 3秒ごとにステータスをチェックします。
+   - 「PDF を変換しています...」と表示されます。
+   - ステータスが `ready` または `failed` になるまで継続します。
+
+5. **結果の表示**
+   - 成功:iframe 内に PDF を表示します。
+   - 失敗:エラーメッセージと代替案(ダウンロード、新しいウィンドウで開く)を表示します。
+
+## テスト手順
+
+1. すべてのサービスを起動します:
+
+   ```bash
+   docker-compose up -d elasticsearch tika libreoffice
+   yarn dev
+   ```
+
+2. PDF 以外のファイル(Word 文書、PPT など)をアップロードします。
+
+3. ファイルの横にある「目」のアイコンをクリックします。
+
+4. 変換プロセスを確認します:
+   - 「PDF を変換しています...」と表示されるはずです。
+   - 数分後、PDF の内容が表示されます。
+   - 失敗した場合は、エラーメッセージと代替案が表示されます。
+
+## サポートされるファイル形式
+
+- Microsoft Office: .doc, .docx, .ppt, .pptx, .xls, .xlsx
+- OpenDocument: .odt, .odp, .ods
+- その他: .rtf, .txt
+
+## 注意事項
+
+- 大容量ファイルの場合、変換には数分かかることがあります。
+- 変換に失敗した場合は、元のファイルのダウンロードを試みてください。
+- 重複した変換を避けるため、一度変換された PDF はキャッシュ(保存)されます。

+ 225 - 0
docs/PROJECT_EXPLANATION_JA.md

@@ -0,0 +1,225 @@
+# Lumina (lumina) 技術および機能アーキテクチャ
+
+## 1. プロジェクト概要
+
+**Lumina (lumina)** は、React と NestJS をベースにしたフルスタックのRAG(検索拡張生成)システムです。ユーザーは多様な形式のドキュメントをアップロードし、カスタム設定でインデックス化を行い、大規模言語モデル(LLM)を用いてナレッジベースに基づいた高度な問答を行うことができます。
+
+最近のアップデートでは、Google NotebookLM に触発された「ナレッジグループ(Notebooks)」機能や「ポッドキャスト生成」機能が追加され、単なる検索システムを超えた学習・分析プラットフォームへと進化しています。
+
+---
+
+## 2. 技術アーキテクチャ
+
+以下は、システムの全体的な技術アーキテクチャを示す図です。
+
+```mermaid
+graph TD
+    User[ユーザー] --> |HTTPS| Frontend[React フロントエンド]
+    Frontend --> |REST API / SSE| Backend[NestJS バックエンド]
+    
+    subgraph "Backend Services"
+        Backend --> |ORM| SQLite[(SQLite DB)]
+        Backend --> |Vector Search| ES[(Elasticsearch)]
+        Backend --> |File Process| Tika[Apache Tika]
+        Backend --> |Doc Convert| LibreOffice[LibreOffice]
+    end
+
+    subgraph "AI Services (External)"
+        Backend --> |API Call| LLM["LLM Provider\n(OpenAI/Gemini/Claude)"]
+        Backend --> |Embedding| Embed["Embedding Model"]
+        Backend --> |Rerank| Rerank["Rerank Model"]
+    end
+```
+
+### 2.1 フロントエンド (Frontend)
+
+モダンなReactエコシステムを採用し、高速でインタラクティブなUIを実現しています。
+
+- **フレームワーク**: React 19 + Vite
+  - 最新のReact機能(Hooks, Context API)を活用。
+  - Viteによる高速な開発サーバーとビルド。
+- **言語**: TypeScript
+  - 型安全性による堅牢なコードベース。
+- **スタイリング**: Tailwind CSS
+  - ユーティリティファーストCSSによる迅速なUI構築。
+- **UIコンポーネント**: Lucide React (アイコン)
+- **状態管理**: React Context + Hooks
+  - `AuthContext`, `LanguageContext` などでグローバル状態を管理。
+- **通信**: Axios + Server-Sent Events (SSE)
+  - RESTful APIとの通信およびAI生成テキストの流式表示(ストリーミング)。
+
+### 2.2 バックエンド (Backend)
+
+スケーラブルでモジュール化されたNode.jsアプリケーションです。
+
+- **フレームワーク**: NestJS
+  - Angularに影響を受けたモジュラーアーキテクチャ。
+  - TypeScriptによる完全な型サポート。
+- **AIオーケストレーション**: LangChain.js
+  - LLM、エンベディング、ベクターストアの統合管理。
+- **データベース**:
+  - **SQLite**: ユーザー情報、設定、ファイルメタデータなどのリレーショナルデータ。
+  - **Elasticsearch**: ドキュメントのベクトル埋め込み(Embedding)と全文検索インデックス。ベクトル次元数の自動検出とインデックス再構築に対応。
+- **認証**: Passport.js + JWT
+  - セキュアなステートレス認証。
+
+### 2.3 インフラ・ファイル処理
+
+- **ファイル解析**:
+  - **Apache Tika**: 「高速モード」でのテキスト抽出。
+  - **Vision Pipeline**: 「精密モード」での画像・レイアウト解析(PDF -> 画像 -> Vision Model)。
+- **ドキュメント変換**: LibreOffice
+  - Office文書(Word, PPTなど)をPDFに変換して処理。
+- **音声生成**: Edge-TTS (または類似サービス)
+  - ポッドキャスト生成機能における音声合成。
+
+---
+
+## 3. 機能アーキテクチャ
+
+システムは以下の主要な機能モジュールで構成されています。
+
+### 3.1 ユーザー管理とセキュリティ
+
+- **認証**: ユーザー登録、ログイン、JWTによるセッション管理。
+- **データ隔離**: 各ユーザーは独自のナレッジベース、設定、チャット履歴を持ち、他ユーザーからはアクセスできません。
+- **多言語UI**: 英語、中国語、日本語のインターフェース切り替えに対応。
+
+### 3.2 知識管理 (Knowledge Management)
+
+- **ファイルアップロード**:
+  - ドラッグ&ドロップによる複数ファイルアップロード。
+  - 対応フォーマット: PDF, Word, Excel, PPT, TXT, MD, 画像など。
+- **処理モード**:
+  - **高速モード (Fast Mode)**: テキストのみを高速に抽出。コスト効率が良い。
+  - **精密モード (Precise Mode)**: ページを画像化し、Visionモデルでレイアウトや図表を含めて解析。
+- **インデックス設定**:
+  - チャンクサイズ(Chunk Size)、オーバーラップ(Overlap)のカスタマイズ。
+  - エンベディングモデルの選択(OpenAI, Geminiなど)。
+
+### 3.3 ナレッジグループ (Knowledge Groups)
+
+- **概念**: ファイルを論理的なグループ(ノートブック)にまとめる機能。
+- **目的**: 特定の研究テーマやプロジェクトごとに資料を整理し、そのグループに限定したチャットが可能。
+- **機能**: グループの作成、編集、削除、ファイルとの関連付け。
+
+### 3.4 RAGチャットシステム
+
+- **ハイブリッド検索**:
+  - ベクトル検索(意味的類似性)とキーワード検索(完全一致)を組み合わせ、リランク(Rerank)モデルで精度を向上。
+- **コンテキスト認識**: ユーザーの質問履歴や現在選択されているナレッジグループを考慮。
+- **流式回答 (Streaming Generation)**: AIの思考過程と回答をリアルタイムで表示。
+- **引用表示**: 回答の根拠となったドキュメントのソースと該当箇所を提示。
+
+### 3.5 ポッドキャスト生成 (Podcasts)
+
+- **概要**: ナレッジグループ内の資料に基づき、AIホストとゲストによる音声対話を生成。
+- **グローバル生成**: 全てのナレッジ、または特定のグループを指定してポッドキャストを作成。
+- **機能**: トピック指定、スクリプト生成(トランスクリプト)、音声再生。
+
+### 3.6 ビジョンモデルによる高度なドキュメント処理 (Advanced Visual Processing)
+
+- **PPT/PDFの視覚的解析**: 従来のテキスト抽出では失われがちなPowerPointやPDFのレイアウト情報、図表、グラフを保持。
+- **Vision Pipeline**:
+  - **ページ画像化**: ドキュメントの各ページを高解像度画像に変換。
+  - **マルチモーダル解析**: GPT-4oやClaude 3.5 Sonnetなどのビジョン対応モデルを使用し、画像内のテキストと視覚要素を統合して理解・説明。
+  - **構造化データ化**: 複雑なスライドや帳票も、人間が見たままの文脈でインデックス化。
+
+### 3.7 インタラクティブなノート作成 (Screenshot & Notes)
+
+- **領域選択とOCR**: PDFプレビュー画面で任意の領域をマウスで矩形選択。
+- **自動テキスト抽出**: 選択範囲の画像からOCR(光学文字認識)でテキストを即座に抽出。
+- **ノート保存**: 抽出したテキストとキャプチャ画像をセットで「ノート」として保存し、後から参照や引用が可能。
+
+### 3.8 システム全体設定 (System Configuration)
+
+- **一元管理ドロワー**: 画面右上の設定アイコンから、システム全体の動作を一括設定。
+- **柔軟なモデル切り替え**:
+  - **LLM**: チャットや推論に使用するメインモデル。
+  - **Embedding**: 検索精度を左右するベクトル化モデル。
+  - **Vision**: 精密モードで使用する画像解析モデル。
+- **即時反映**: 設定変更はシステム全体に即座に適用され、再起動なしで異なるモデルの挙動をテスト可能。
+
+### 3.9 モデル管理 (Model Management)
+
+- **BYOK (Bring Your Own Key)**: ユーザー自身のAPIキーを設定可能。
+- **一元管理**: 「システム構成(System Settings)」ドロワーから、グローバルなモデル設定(LLM, Embedding, Rerank, Vision)を一括管理。
+- **マルチプロバイダー**: OpenAI, Google Gemini, Anthropic (Claude), Ollama などのモデル設定をサポート。
+- **カスタム設定**: Temperature, Top-K, Max Tokens などの推論パラメータを調整可能。
+
+---
+
+## 4. データフロー
+
+### 4.1 ドキュメント取り込みフロー
+
+ドキュメントがアップロードされてから検索可能になるまでの処理フローです。
+
+```mermaid
+sequenceDiagram
+    participant U as User
+    participant BE as Backend
+    participant TP as Tika/Vision
+    participant EM as "Embedding Model"
+    participant ES as Elasticsearch
+
+    U->>BE: ファイルアップロード
+    BE->>BE: ファイルタイプ判別
+    
+    rect rgb(240, 248, 255)
+        alt 高速モード
+            BE->>TP: テキスト抽出 (Tika)
+            TP-->>BE: 抽出テキスト
+        else 精密モード
+            BE->>BE: PDF/画像変換
+            BE->>TP: 画像解析 (Vision API)
+            TP-->>BE: 構造化テキスト
+        end
+    end
+
+    BE->>BE: チャンキング (分割)
+    loop 各チャンク
+        BE->>EM: ベクトル化リクエスト
+        EM-->>BE: ベクトルデータ
+    end
+
+    BE->>ES: インデックス保存 (ベクトル + メタデータ)
+    BE-->>U: 処理完了通知
+```
+
+1. **アップロード**: ユーザーがファイルを送信。
+1. **前処理**: ファイルタイプに応じた変換(例: docx -> pdf)。
+1. **解析**:
+    - (高速モード) Tikaでテキスト抽出。
+    - (精密モード) PDFを画像化 -> Vision APIで解析。
+1. **チャンキング**: 設定されたルールでテキストを分割。
+1. **埋め込み**: Embedding APIでベクトル化。
+1. **保存**: ベクトルとメタデータをElasticsearchに保存。
+
+### 4.2 RAG検索・生成フロー
+
+ユーザーの質問から回答生成までのRAGプロセスフローです。
+
+```mermaid
+flowchart LR
+    Q[ユーザーの質問] --> Embed[質問のベクトル化]
+    Embed --> Search[ベクトル検索 + キーワード検索]
+    Search --> ES[(Elasticsearch)]
+    ES --> Results[検索結果候補]
+    
+    Results --> Rerank{"リランク有効?"}
+    Rerank -- Yes --> RerankModel[Rerankモデル]
+    RerankModel --> TopK[上位結果抽出]
+    Rerank -- No --> TopK
+    
+    TopK --> Prompt["プロンプト構築\n(質問 + コンテキスト)"]
+    Prompt --> LLM[LLM生成]
+    LLM --> Stream[流式回答出力]
+```
+
+1. **クエリ受信**: ユーザーの質問を受け取る。
+1. **検索**: 質問をベクトル化し、Elasticsearchで類似チャンクを検索(+キーワード検索)。
+1. **リランク (Optional)**: 検索結果をRerankモデルで再評価し、関連度順に並べ替え。
+1. **プロンプト構築**: 上位のチャンクをコンテキストとしてシステムプロンプトに組み込む。
+1. **生成**: LLMにプロンプトを送信し、回答を生成。
+1. **レスポンス**: 回答と参照ソースをフロントエンドにストリーミング送信。

BIN
docs/PROJECT_EXPLANATION_JA.pdf


+ 316 - 0
docs/QUICK_START.md

@@ -0,0 +1,316 @@
+# クイックスタートガイド
+
+## 🚀 5分でクイック起動
+
+### 1. 環境設定
+
+```bash
+# プロジェクトディレクトリに移動
+cd /home/fzxs/workspaces/demo/lumina
+
+# 環境設定ファイルの作成
+cp server/.env.sample server/.env
+
+# 設定の編集
+vim server/.env
+```
+
+### 2. 依存関係のインストール
+
+```bash
+yarn install
+```
+
+### 3. サービスの起動
+
+```bash
+# 基本サービスの起動
+docker-compose up -d elasticsearch tika libreoffice
+
+# 開発サーバーの起動
+yarn dev
+```
+
+### 4. サービスの検証
+
+```bash
+# サービスの状態を確認
+docker-compose ps
+
+# 期待される出力:
+# NAME                COMMAND                  STATUS
+# local-es            ...                      Up
+# lumina-tika         ...                      Up
+# lumina-libreoffice  ...                      Up
+```
+
+<http://localhost:5173> にアクセスして開始してください!
+
+## 📝 利用フロー
+
+### ステップ1: システムへログイン
+
+1. <http://localhost> にアクセスします。
+2. 既存のアカウントでログインするか、新しいアカウントを登録します。
+
+### ステップ2: モデルの設定
+
+1. 「モデル管理」に移動します。
+2. Vision モデルを追加します (OpenAI/Gemini をサポート)。
+3. API キーを設定します。
+4. デフォルトの Vision モデルとして設定します。
+
+### ステップ3: ドキュメントのアップロード
+
+1. 「ドキュメントのアップロード」をクリックします。
+2. PDF/Word/PPT ファイルを選択します。
+3. アップロード用モーダルウィンドウで:
+   - Embedding(埋め込み)モデルを選択します。
+   - **処理モードを選択します**:
+     - ⚡ 高速モード: テキストのみを抽出
+     - 🎯 高精度モード: 画像とテキストを混合して分析
+   - チャンク設定を調整します (任意)。
+4. 「処理開始」をクリックします。
+
+### ステップ4: 結果の確認
+
+1. バックエンドの処理が完了するまで待ちます。
+2. ファイルの状態を確認します: 「処理中」 → 「抽出完了」 → 「ベクトル化完了」
+3. チャット画面で RAG 検索をテストします。
+
+## 🔍 モード選択ガイド
+
+### 高速モードを使用する場合
+
+✅ **推奨シーン:**
+
+- テキストのみのドキュメント
+- コードファイル
+- シンプルな Markdown
+- 画像コンテンツが不要な場合
+
+**メリット:**
+
+- 処理が速い (数秒)
+- 追加コストがかからない
+- 安定していて信頼性が高い
+
+### 高精度モードを使用する場合
+
+✅ **推奨シーン:**
+
+- PDF ドキュメント (画像とテキストが混在しているもの)
+- Word/PPT (グラフや表が含まれているもの)
+- レイアウト情報を保持する必要がある場合
+- 重要なドキュメント
+
+**メリット:**
+
+- 画像コンテンツを保持
+- グラフや表を認識
+- ページのレイアウトを保持
+- インデックスの質が高い
+
+**注意点:**
+
+- API 利用料が必要 (目安: $0.01/ページ)
+- 処理に時間がかかる
+- Vision モデルの設定が必要
+
+## 💰 コスト管理
+
+### 利用枠(クォータ)の確認
+
+```bash
+# ユーザーのクォータを照会
+GET /api/users/:userId/quota
+
+# レスポンス例
+{
+  "monthlyCost": 15.50,
+  "maxCost": 100,
+  "remaining": 84.50,
+  "usagePercent": 15.5
+}
+```
+
+### コスト見積もり
+
+| ファイルタイプ | サイズ | ページ数 | 予想コスト | 予想時間 |
+|---------|------|------|---------|---------|
+| PDF | 10MB | ~20ページ | $0.20 | 60秒 |
+| Word | 5MB | ~10ページ | $0.10 | 30秒 |
+| PPT | 15MB | ~30ページ | $0.30 | 90秒 |
+
+### クォータの管理
+
+```sql
+-- 管理者によるクォータのリセット
+UPDATE users SET monthly_cost = 0 WHERE id = 'user-uuid';
+
+-- クォータ制限の調整
+UPDATE users SET max_cost = 200 WHERE id = 'user-uuid';
+```
+
+## 🐛 トラブルシューティング
+
+### 問題1: LibreOffice サービスが利用できない
+
+**症状:**
+
+```
+❌ LibreOffice 健康診断: 失敗
+```
+
+**解決策:**
+
+```bash
+docker-compose up -d libreoffice
+docker-compose logs libreoffice
+```
+
+### 問題2: ImageMagick がインストールされていない
+
+**症状:**
+
+```
+❌ convert: command not found
+```
+
+**解決策:**
+
+```bash
+# server イメージを再ビルド
+docker build -t lumina-server:latest ./server/
+docker-compose up -d server
+```
+
+### 問題3: クォータ不足
+
+**症状:**
+
+```
+❌ クォータ不足: 残り $5.00, 必要 $10.00
+```
+
+**解決策:**
+
+- 翌月の自動リセットを待つ
+- 管理者に連絡してクォータを増やす
+- 高速モードで処理する
+
+### 問題4: 一時ファイルが多すぎる
+
+**症状:**
+
+```
+⚠️  100個以上の一時ファイルが見つかりました
+```
+
+**解決策:**
+
+```bash
+# 手動でクリーンアップ
+rm -rf temp/*
+
+# または .env で自動クリーンアップを設定
+TEMP_CLEANUP=true
+```
+
+## 📊 モニタリングとログ
+
+### 処理ログを確認する
+
+```bash
+# リアルタイムログ
+docker-compose logs -f server
+
+# Vision Pipeline のログをフィルタリング
+docker-compose logs -f server | grep "Vision\|高精度モード\|コスト"
+```
+
+### 主要なログの例
+
+```
+✅ 高精度モードでの処理を開始
+✅ 予想コスト: $0.15, 予想時間: 45秒
+✅ クォータチェックに合格
+✅ PDFを画像に変換: 15ページ
+✅ Vision 分析: 15/15 成功
+✅ 実際のコストを差し引きました: $0.15
+✅ 処理完了: 所要時間 42秒
+```
+
+## 🎯 ベストプラクティス
+
+### 1. ドキュメントの準備
+
+- **ファイルサイズの最適化**: 大容量ファイルは分割して処理します。
+- **鮮明な画像**: 高品質な画像の方が認識精度が高まります。
+- **標準フォーマット**: PDF > Word > PPT の順で推奨されます。
+
+### 2. モードの選択
+
+- **小規模ファイル (<10MB)**: 高精度モード
+- **大規模ファイル (>50MB)**: 分割するか、高速モードを検討
+- **テキストのみ**: 高速モード
+- **画像・テキスト混在**: 高精度モード
+
+### 3. コスト削減
+
+- **使用率の監視**: 75%で警告、90%で重大な警告を表示します。
+- **定期的な整理**: 不要なドキュメントを削除します。
+- **バッチ処理**: 適切なバッチサイズを使用して効率化します。
+
+### 4. パフォーマンスの最適化
+
+- **チャンクサイズ**: 200-500 トークン
+- **オーバーラップ率**: 10-20%
+- **バッチサイズ**: 50-100
+
+## 📞 テクニカルサポート
+
+### ドキュメントの参照
+
+- 詳細な実装: `docs/VISION_PIPELINE_IMPLEMENTATION.md`
+- API ドキュメント: `VISION_PIPELINE_SUMMARY.md`
+- テストスクリプト: `server/test-*.ts`
+
+### デバッグツール
+
+```bash
+# LibreOffice のテスト
+curl http://localhost:8100/health
+
+# Tika のテスト
+curl http://localhost:9998
+
+# Elasticsearch のテスト
+curl http://localhost:9200
+
+# コンテナの状態を確認
+docker-compose ps
+```
+
+## ✅ チェックリスト
+
+起動前のチェック項目:
+
+- [ ] Docker と Docker Compose がインストールされている
+- [ ] ポート 80, 3001, 8100, 9200, 9998 が他で使用されていない
+- [ ] server/.env が設定されている
+- [ ] Vision モデルの API キーが用意されている
+- [ ] 少なくとも 4GB のメモリが使用可能
+- [ ] 十分なディスク容量がある (>5GB)
+
+使用前のチェック項目:
+
+- [ ] すべてのサービスの状態が Up になっている
+- [ ] Vision モデルが設定されている
+- [ ] Embedding(埋め込み)モデルが設定されている
+- [ ] クォータが十分にある
+- [ ] テスト用ファイルが用意されている
+
+---
+
+**完了です!** これで Vision Pipeline システムを使用して、画像とテキストが混在したドキュメントを処理する準備が整いました!🎉

+ 87 - 0
docs/RAG_COMPLETE_IMPLEMENTATION.md

@@ -0,0 +1,87 @@
+# RAG 機能の完全実装ドキュメント
+
+## 実装完了 ✅
+
+### バックエンドの実装
+
+- **RagService**: コアとなる RAG ロジック。ベクトル検索とプロンプト構築をサポート。
+- **RagModule**: モジュール化されたカプセル化。
+- **API エンドポイント**: `POST /api/knowledge-bases/rag-search`
+- **類似度フィルタリング**: 動的なしきい値設定。
+- **LangChain 統合**: プロンプトテンプレートの管理。
+
+### フロントエンドの実装
+
+- **設定パネル**: 類似度しきい値スライダー (0.1-1.0)
+- **RAG サービス**: API 呼び出しのカプセル化。
+- **チャット統合**: 自動 RAG 検索と拡張。
+- **検索ステータス**: 「ナレッジベースを検索中...」のヒント表示。
+- **結果表示**: SearchResultsPanel コンポーネント。
+
+## コアフロー
+
+### 1. ユーザーの質問
+
+```
+ユーザーが質問を入力 → RAG 検索がトリガーされる
+```
+
+### 2. RAG 検索
+
+```
+質問のベクトル化 → ES ベクトル検索 → 類似度フィルタリング → 拡張プロンプトの構築
+```
+
+### 3. LLM 生成
+
+```
+拡張プロンプト → LLM 推論 → 出典が付与された回答
+```
+
+### 4. 結果の表示
+
+```
+回答の表示 + [ファイル名.pdf] + 検索されたセグメントの確認
+```
+
+## 主要な特徴
+
+### ✅ インテリジェント検索
+
+- ユーザーが選択した Embedding モデルを使用。
+- 類似度しきい値によるフィルタリングをサポート。
+- ファイルごとにグループ化して結果を表示。
+
+### ✅ 拡張生成
+
+- RAG プロンプトを自動構築。
+- ドキュメントのコンテキストと出典情報を含める。
+- 多言語での回答をサポート。
+
+### ✅ ユーザー体験
+
+- 検索プロセスの可視化。
+- 具体的な検索セグメントの確認が可能。
+- 自動的な出典の付与。
+- 関連コンテンツがない場合の明確な通知。
+
+### ✅ 柔軟な設定
+
+- 動的な類似度しきい値。
+- topK 結果数の制御。
+- 再ランキングのサポート(有効な場合)。
+
+## 利用方法
+
+1. **ドキュメントのアップロード** → 自動的にベクトルインデックスを作成。
+2. **設定の調整** → 類似度しきい値、topK など。
+3. **質問** → 自動的に RAG 検索と拡張を実行。
+4. **結果の確認** → 出典付きのインテリジェントな回答。
+5. **セグメントの確認** → 検索アイコンをクリックして具体的な内容を表示。
+
+## 技術スタック
+
+- **バックエンド**: NestJS + LangChain + Elasticsearch
+- **フロントエンド**: React + TypeScript
+- **ベクトル化**: 多様な Embedding モデルをサポート
+- **検索**: コサイン類似度 + しきい値フィルタリング

+ 249 - 0
docs/SIMILARITY_SCORE_BUGFIX.md

@@ -0,0 +1,249 @@
+# 相似度スコアが 100% を超えるバグの修正
+
+## 🐛 問題の記述
+
+ユーザーがチャットインターフェースにて、引用ソースの適合度スコアが 100% を超えている現象を確認しました。これは数学的に不可能です(相似度スコアは 0〜100% の間であるべきです)。
+
+**発生していた現象:**
+
+```
+引用元表示:適合度 123.5%
+          適合度 165.2%
+          適合度 201.8%
+```
+
+## 🔍 根本原因の分析
+
+### 問題の発生源
+
+Elasticsearch が返す生のスコア(`_score`)は、特に以下の場合に 1.0 を超えることがあります:
+
+1. **ベクトル検索 (Vector Search)**:コサイン類似度を使用しますが、戻り値が 1.0 を超える場合があります。
+2. **全文検索 (Full-text Search)**:TF-IDF スコアが非常に大きくなる場合があります。
+3. **ハイブリッド検索 (Hybrid Search)**:ウェイトを組み合わせた後の合計が 1.0 を超える場合があります。
+
+### データフローの分析
+
+```
+Elasticsearch がスコアを返却 (_score = 1.5)
+    ↓
+elasticsearch.service.ts: searchSimilar() / searchFullText()
+    ↓
+chat.service.ts: hybridSearch()
+    ↓
+ChatService: result.score を返却
+    ↓
+フロントエンド ChatInterface.tsx: (source.score * 100).toFixed(1)%
+    ↓
+表示:150% ❌
+```
+
+### 問題のあったコード
+
+**elasticsearch.service.ts - hybridSearch メソッド:**
+
+```typescript
+// 問題:Elasticsearch の _score をそのまま使用しており、1.0 を超える可能性がある
+vectorResults.forEach((result) => {
+  combinedResults.set(result.id, {
+    ...result,
+    vectorScore: result.score,  // 例えば 1.5 になる可能性がある
+    textScore: 0,
+    combinedScore: result.score * vectorWeight,  // 1.5 * 0.7 = 1.05
+  });
+});
+```
+
+**ChatInterface.tsx - 表示ロジック:**
+
+```typescript
+// 問題:スコアが 0〜1 の間であることを前提に 100 倍している
+{(source.score * 100).toFixed(1)}%  // 1.05 * 100 = 105%
+```
+
+## ✅ 解決策
+
+### 1. ElasticsearchService にスコアの正規化を追加
+
+**新規メソッド `normalizeScore` の追加:**
+
+```typescript
+private normalizeScore(rawScore: number): number {
+  if (!rawScore || rawScore <= 0) return 0.5;
+
+  // 広範囲のスコアを処理するため、対数正規化を使用
+  const logScore = Math.log10(rawScore + 1);
+
+  // 0.5〜1.0 の範囲にマッピング
+  const normalized = 0.5 + (logScore * 0.25);
+
+  // 最終的に 0.5〜1.0 の間に制限
+  return Math.max(0.5, Math.min(1.0, normalized));
+}
+```
+
+**なぜ対数正規化を使用するのか?**
+
+- Elasticsearch のスコア範囲:1〜100 以上
+- log10(1) = 0 → 0.5
+- log10(10) = 1 → 0.75
+- log10(100) = 2 → 1.0
+- 結果が常に 0.5〜1.0 の間に収まるようになります。
+
+### 2. すべての検索メソッドで正規化を適用
+
+**searchSimilar メソッド:**
+
+```typescript
+const results = response.hits.hits.map((hit: any) => ({
+  id: hit._id,
+  score: this.normalizeScore(hit._score),  // ✅ 正規化を適用
+  // ...
+}));
+```
+
+**searchFullText メソッド:**
+
+```typescript
+const results = response.hits.hits.map((hit: any) => ({
+  id: hit._id,
+  score: this.normalizeScore(hit._score),  // ✅ 正規化を適用
+  // ...
+}));
+```
+
+**hybridSearch メソッド:**
+
+```typescript
+// 結合された全スコアを取得して最大・最小を確認
+const allScores = Array.from(combinedResults.values()).map(r => r.combinedScore);
+const maxScore = Math.max(...allScores, 1);
+const minScore = Math.min(...allScores);
+
+// 総合スコアでソートして上位 topK を取得し、0〜1 の範囲に正規化
+return Array.from(combinedResults.values())
+  .sort((a, b) => b.combinedScore - a.combinedScore)
+  .slice(0, topK)
+  .map((result) => {
+    // Min-Max 正規化
+    let normalizedScore = (result.combinedScore - minScore) / (maxScore - minScore);
+
+    // 0.5〜1.0 の範囲にマッピング
+    normalizedScore = 0.5 + (normalizedScore * 0.5);
+
+    // 0.5〜1.0 の間に制限
+    normalizedScore = Math.max(0.5, Math.min(1.0, normalizedScore));
+
+    return {
+      ...result,
+      score: normalizedScore,
+    };
+  });
+```
+
+### 3. フロントエンドの表示ロジック(変更なし)
+
+バックエンド側でスコアが 0〜1 の間に収まることを保証したため、フロントエンドの修正は不要です:
+
+```typescript
+{(source.score * 100).toFixed(1)}%  // 常に 50.0% 〜 100.0% が表示される
+```
+
+## 📊 修正後の効果
+
+### 修正前
+
+| 元のスコア | 表示結果 | 問題点 |
+|---------|---------|------|
+| 1.5 | 150% | ❌ 100% を超える |
+| 2.0 | 200% | ❌ 100% を超える |
+| 0.8 | 80% | ✅ 正常 |
+
+### 修正後
+
+| 元のスコア | 正規化後 | 表示結果 | ステータス |
+|---------|---------|---------|------|
+| 1.5 | 0.875 | 87.5% | ✅ |
+| 2.0 | 0.938 | 93.8% | ✅ |
+| 0.8 | 0.750 | 75.0% | ✅ |
+
+## 🧪 テスト・検証
+
+### テスト手順
+
+1. **テストドキュメントのアップロード**
+
+   ```bash
+   # 異なる内容を含むテストドキュメントを作成
+   echo "人工知能 機械学習 深層学習" > test1.txt
+   echo "Python JavaScript TypeScript" > test2.txt
+   ```
+
+2. **検索クエリの実行**
+   - クエリ:「人工知能」
+   - 期待値:関連ドキュメントが表示され、スコアが 50〜100% の間であること。
+
+3. **スコア範囲の検証**
+
+   ```typescript
+   // ブラウザのコンソールでチェック
+   console.log('すべてのスコアが 50〜100 の間であるべきです');
+   sources.forEach(s => {
+     if (s.score * 100 > 100) console.error('スコアが 100% を超えています:', s);
+   });
+   ```
+
+### 期待される結果
+
+- ✅ すべての相似度スコアが 50.0% 〜 100.0% の間にある。
+- ✅ 関連性の高いドキュメントは 100% に近い値を示す。
+- ✅ 関連性の低いドキュメントは 50% に近い値を示す。
+- ✅ 100% を超えるスコアは表示されない。
+
+## 📝 修正ファイル
+
+### バックエンド
+
+- `server/src/elasticsearch/elasticsearch.service.ts`
+  - プライベートメソッド `normalizeScore()` を追加
+  - `searchSimilar()` にて正規化を適用
+  - `searchFullText()` にて正規化を適用
+  - `hybridSearch()` にて正規化を適用
+
+### フロントエンド
+
+- 修正なし(バックエンドでスコア範囲を保証)
+
+## ⚠️ 注意事項
+
+### 1. スコアの意味の変化
+
+修正後、スコアは Elasticsearch の生の相似度を直接示すのではなく、以下の目安となります:
+
+- **50-60%**:低い関連性
+- **60-75%**:中程度の関連性
+- **75-90%**:高い関連性
+- **90-100%**:非常に高い関連性
+
+### 2. しきい値の調整
+
+以前に相似度フィルタリング(例:`similarityThreshold: 0.7`)を使用していた場合、調整が必要になる可能性があります:
+
+```typescript
+// 旧設定(生のスコアベース)
+similarityThreshold: 0.7
+
+// 新設定(正規化スコアベース)
+similarityThreshold: 0.6  // 以前の 0.7 に相当する目安
+```
+
+### 3. パフォーマンスへの影響
+
+- 正規化の計算は非常に軽量です (O(1))。
+- 検索パフォーマンスへの影響はありません。
+
+## 📚 参考文献
+
+- [Elasticsearch Similarity Scoring](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-script-score-query.html)
+- [Vector Search Cosine Similarity](https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html)
+- [Min-Max Normalization](https://en.wikipedia.org/wiki/Feature_scaling#Rescaling_(min-max_normalization))

+ 158 - 0
docs/SUPPORTED_FILE_TYPES.md

@@ -0,0 +1,158 @@
+# サポートされているファイル形式
+
+本システムは Apache Tika を使用してドキュメントを解析しており、数百種類のファイル形式をサポートしています。
+
+## 📋 サポートファイル形式一覧
+
+### 📄 PDF ドキュメント
+
+- `application/pdf` - PDF ドキュメント
+
+### 📝 Microsoft Office ドキュメント
+
+- `application/msword` - Word ドキュメント (.doc)
+- `application/vnd.openxmlformats-officedocument.wordprocessingml.document` - Word ドキュメント (.docx)
+- `application/vnd.ms-excel` - Excel スプレッドシート (.xls)
+- `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` - Excel スプレッドシート (.xlsx)
+- `application/vnd.ms-powerpoint` - PowerPoint プレゼンテーション (.ppt)
+- `application/vnd.openxmlformats-officedocument.presentationml.presentation` - PowerPoint プレゼンテーション (.pptx)
+
+### 📊 OpenOffice / LibreOffice ドキュメント
+
+- `application/vnd.oasis.opendocument.text` - テキストドキュメント (.odt)
+- `application/vnd.oasis.opendocument.spreadsheet` - スプレッドシート (.ods)
+- `application/vnd.oasis.opendocument.presentation` - プレゼンテーション (.odp)
+- `application/vnd.oasis.opendocument.graphics` - グラフィックドキュメント (.odg)
+
+### 📝 テキストファイル
+
+- `text/plain` - プレーンテキスト (.txt)
+- `text/markdown` - Markdown (.md, .markdown)
+- `text/html` - HTML ドキュメント (.html, .htm)
+- `text/csv` - CSV 表形式 (.csv)
+- `text/xml` - XML ドキュメント (.xml)
+- `application/xml` - XML ドキュメント
+- `application/json` - JSON データ (.json)
+
+### 💻 コードファイル
+
+- `text/x-python` - Python コード (.py)
+- `text/x-java` - Java コード (.java)
+- `text/x-c` - C コード (.c)
+- `text/x-c++` - C++ コード (.cpp, .cc, .cxx)
+- `text/javascript` - JavaScript コード (.js)
+- `text/typescript` - TypeScript コード (.ts)
+
+### 🖼️ 画像ファイル
+
+- `image/jpeg` - JPEG 画像 (.jpg, .jpeg)
+- `image/png` - PNG 画像 (.png)
+- `image/gif` - GIF 画像 (.gif)
+- `image/webp` - WebP 画像 (.webp)
+- `image/tiff` - TIFF 画像 (.tiff, .tif)
+- `image/bmp` - BMP 画像 (.bmp)
+- `image/svg+xml` - SVG ベクター画像 (.svg)
+
+### 📦 圧縮ファイル
+
+- `application/zip` - ZIP 圧縮アーカイブ (.zip)
+- `application/x-tar` - TAR アーカイブ (.tar)
+- `application/gzip` - GZIP 圧縮 (.gz)
+- `application/x-7z-compressed` - 7z 圧縮アーカイブ (.7z)
+
+### 📚 その他のドキュメント形式
+
+- `application/rtf` - RTF ドキュメント (.rtf)
+- `application/epub+zip` - EPUB 電子書籍 (.epub)
+- `application/x-mobipocket-ebook` - MOBI 電子書籍 (.mobi)
+
+## 🔧 自動サポートルール
+
+明示的なリスト以外にも、システムは以下のパターンを自動的にサポートします:
+
+1. **すべてのテキストタイプ** - `text/` で始まるすべての MIME タイプ
+2. **Office ドキュメント** - `application/vnd.` で始まるすべてのタイプ
+3. **その他の形式** - `application/x-` で始まるすべてのタイプ
+
+これは、特定の形式がリストになくても、Tika が解析可能であればシステムで処理できることを意味します。
+
+## ⚠️ 注意事項
+
+### 画像処理
+
+- 画像ファイルから意味のある内容を抽出するには、**ビジョンモデル**の設定が必要です。
+- ビジョンモデルが設定されていない場合、システムはファイル名をコンテンツとして使用します。
+- 「システム設定」でビジョンをサポートする LLM(GPT-4V、Gemini など)を設定することをお勧めします。
+
+### 大容量ファイルの処理
+
+- ファイルサイズ制限:デフォルト 100MB(`.env` の `MAX_FILE_SIZE` で設定可能)
+- 大容量ファイルはバッチ処理され、メモリオーバーフローを防止します。
+- 推奨:最適なパフォーマンスを得るために、1ファイルあたり 50MB 以下にすることをお勧めします。
+
+### エンコーディングの問題
+
+- システムはファイルのエンコーディングを自動検出します。
+- UTF-8 エンコーディングのテキストファイルを推奨します。
+- UTF-8 以外のエンコーディングでは文字化けが発生する可能性があります。
+
+## 📝 設定例
+
+### 環境変数の設定
+
+```env
+# ファイルアップロードの制限
+MAX_FILE_SIZE=104857600  # 100MB
+
+# チャンク設定(Embeddingモデルに合わせて調整)
+MAX_CHUNK_SIZE=8191      # OpenAI embedding-3-large
+MAX_OVERLAP_SIZE=200
+```
+
+### モデルの設定
+
+フロントエンドの「システム設定」→「モデル管理」で Embedding モデルを設定する際:
+
+- **最大入力 (Tokens)**: モデルの設定に従う(OpenAI=8191, Gemini=2048)
+- **ベクトル次元数**: モデルの出力設定に従う(text-embedding-3-large=2560, text-embedding-3-small=1536)
+- **バッチ処理制限**: モデルの設定に従う(OpenAI=2048, Gemini=100)
+
+## 🔍 トラブルシューティング
+
+### ファイル形式がサポートされていない
+
+**エラー**: `不支持的文件类型: application/xxx` (サポートされていないファイル形式)
+
+**解決策**:
+
+1. ファイル形式がサポートリストに含まれているか確認してください。
+2. ファイルの拡張子が正しいか確認してください。
+3. テキストエディタで開き、内容が読み取れるか確認してください。
+4. 新しい形式のサポートが必要な場合は、Issue を送信してください。
+
+### 解析に失敗する
+
+**エラー**: `无法提取文本内容` (テキスト内容を抽出できません)
+
+**解決策**:
+
+1. Apache Tika サービスが動作しているか確認してください。
+2. Tika のログを確認してください:`docker-compose logs tika`
+3. 他のツールでファイルを開き、ファイルが破損していないか確認してください。
+4. ファイルの権限を確認してください。
+
+### エンコーディングの問題
+
+**現象**: テキストが文字化けする
+
+**解決策**:
+
+1. ファイルを UTF-8 エンコーディングに変換してください。
+2. テキストエディタで再度保存してください。
+3. システムの言語設定を確認してください。
+
+## 📚 参考文献
+
+- [Apache Tika 公式ドキュメント](https://tika.apache.org/1.24/formats.html)
+- [Tika サポート形式一覧](https://tika.apache.org/1.24/formats.html)
+- [MIME タイプ標準](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types)

+ 55 - 0
docs/VECTOR_DB_COMPARISON_JA.md

@@ -0,0 +1,55 @@
+# Elasticsearch vs Chroma 比較分析
+
+Elasticsearch と Chroma は、現在人気のあるベクトルストレージソリューションですが、その設計思想と適用シナリオには大きな違いがあります。
+
+**simple-kb** のようなナレッジベースプロジェクトにおいて、Elasticsearch (ES) を選択した主な理由は、その強力な **ハイブリッド検索 (Hybrid Search)** 能力を活用するためです。
+
+以下に、両者の詳細な長所と短所の比較を示します。
+
+## コア機能の比較まとめ
+
+| 機能 | Elasticsearch (ES) | Chroma |
+| :--- | :--- | :--- |
+| **位置付け** | 汎用検索エンジン(全文検索 + ベクトル検索) | AI ネイティブ ベクトルデータベース |
+| **コアな強み** | **ハイブリッド検索** (BM25 + kNN)、強力なメタデータフィルタリング | **軽量で使いやすい**、Python 和性が高い、LLM 専用設計 |
+| **全文検索** | 👑 **業界標準** (BM25)、形態素解析、曖昧検索などをサポート | 弱い (主にベクトルの類似度に依存、テキスト検索は限定的) |
+| **リソース消費** | 🔴 **高** (Java ヒープメモリ、起動に通常 1GB+ メモリが必要) | 🟢 **極めて低い** (軽量プロセス、インメモリ実行も可能) |
+| **デプロイ・保守** | 🔴 複雑 (Java 環境、設定項目が多い) | 🟢 簡単 (`pip install` または軽量 Docker) |
+| **拡張性** | 分散クラスタが成熟しており、PB 級のデータをサポート | シングルノードは強力だが、分散クラスタ機能は比較的新しい |
+| **エコシステム** | 非常に豊富 (Kibana 可視化, Logstash など) | AI / LangChain エコシステムに特化 |
+
+---
+
+## 1. Elasticsearch の長所と短所 (なぜ simple-kb で採用したのか?)
+
+**長所:**
+
+* **ハイブリッド検索 (Hybrid Search) - 決定的な機能**: RAG システムにおける最大の課題は「専門用語が検索できない」ことです。
+  * **ベクトル検索**は、意味の理解に優れています(例:「スマホ」で「iPhone」を検索可能)。
+  * **キーワード検索 (ES)** は、正確な一致に優れています(例:エラーコード「Error 503」や特定の型番「RTX 4090」)。
+  * ES はこれらを同時に実行し、スコアを加重して統合できます。これが現在の RAG システムの精度向上の鍵となります。
+* **強力なメタデータフィルタリング**: ベクトル検索の前後に、ユーザー権限、ファイルタイプ、時間範囲などのフィールドに基づいて、非常に効率的にデータをフィルタリングできます。
+* **成熟と安定**: ビッグデータ分野で10年以上の実績があります。
+
+**短所:**
+
+* **重い**: JVM ベースであり、メモリを消費します。個人開発者の小型 VPS で ES コンテナを実行するのは少し厳しい場合があります。
+* **学習コストが高い**: DSL クエリ構文が複雑で、設定が煩雑です。
+
+## 2. Chroma の長所と短所
+
+**長所:**
+
+* **開発者体験 (DX) が最高**: 「AI Native」です。API 設計が Python 開発者の直感に非常に合っており、ES のような複雑な JSON クエリを書く必要がありません。
+* **軽量**: プロトタイプの迅速な開発 (PoC)、ローカルで動作する Agent、または中小規模のアプリケーションに最適です。
+* **Embedding 内蔵**: Chroma はシンプルな Embedding モデルを簡単に内蔵でき、すぐに使用可能です。
+
+**短所:**
+
+* **キーワード検索能力が弱い**: ユーザーが Embedding モデルにとって未知の非常に具体的な単語(例:社内のプロジェクトコード名)を検索する場合、純粋なベクトル類似度では検索が難しく、ES のような転置インデックスによる検索が必要です。
+* **機能が単一**: 基本的にベクトルストレージ専用です。システムがログ保存や通常の検索も必要とする場合、別途データベースを用意する必要があります。
+
+## 結論:simple-kb における選択
+
+* **現在のアーキテクチャ (ES)**: **本番環境レベルの正確性**を選択しました。デプロイは少し手間ですが(Docker が必要)、システムが「意味的な曖昧さ」や「キーワードの正確な検索」に直面した際に、優れたパフォーマンスを発揮することを保証します。
+* **もし Chroma に変更した場合**: システムのデプロイは非常に簡単になりますが(Docker コンテナさえ不要で、Python プロセスに組み込み可能)、特定の専門用語を扱う際に BM25 キーワード検索の補助がないため、**再現率(Recall)**が低下する可能性があります。

BIN
docs/VECTOR_DB_COMPARISON_JA.pdf


+ 265 - 0
docs/VISION_PIPELINE_COMPLETE.md

@@ -0,0 +1,265 @@
+# Vision Pipeline 完全実装
+
+## 🎯 概要
+
+Vision Pipeline は、画像とテキストが混在したドキュメントを処理するためのシステムの「高精度モード」機能です。LibreOffice による変換、ImageMagick による画像処理、および Vision モデルによる分析を通じて、完全なドキュメント内容の抽出を実現します。
+
+### デュアルモードの比較
+
+| 特徴 | 高速モード | 高精度モード |
+|------|---------|---------|
+| 処理ツール | Apache Tika | Vision Pipeline |
+| 画像処理 | ❌ スキップ | ✅ 完全な分析 |
+| 処理速度 | 高速 | 低速 |
+| コスト | 無料 | 約 $0.01/ページ |
+| 適用シーン | テキストのみのドキュメント | 画像・テキスト混在ドキュメント |
+
+## 🏗️ 技術アーキテクチャ
+
+### コアフロー
+
+```
+ドキュメントのアップロード → LibreOffice 変換 → PDF を画像化 → Vision 分析 → ベクトルインデックス
+```
+
+### サービスコンポーネント
+
+#### 1. LibreOffice サービス (FastAPI)
+
+- **ポート**: 8100
+- **機能**: ドキュメント形式の統一化 (Word/PPT/Excel → PDF)
+- **API ドキュメント**: <http://localhost:8100/docs>
+
+```python
+# libreoffice-server/main.py
+from fastapi import FastAPI, File, UploadFile
+from pydantic import BaseModel
+
+app = FastAPI(title="ドキュメント変換サービス")
+
+@app.post("/convert")
+async def convert(file: UploadFile = File(...)):
+    # 変換ロジック
+    return {"pdf_path": "...", "converted": True}
+
+@app.get("/health")
+async def health():
+    return {"status": "healthy"}
+```
+
+#### 2. PDF2Image サービス (Node.js)
+
+```typescript
+// server/src/pdf2image/pdf2image.service.ts
+@Injectable()
+export class Pdf2ImageService {
+  async convertToImages(pdfPath: string): Promise<string[]> {
+    // ImageMagick を使用して変換
+    const images = await this.imagemagick.convert(pdfPath, {
+      density: 300,
+      format: 'jpeg',
+      quality: 85
+    });
+    return images;
+  }
+}
+```
+
+#### 3. Vision サービス
+
+```typescript
+// server/src/vision/vision.service.ts
+@Injectable()
+export class VisionService {
+  async analyzeImage(imagePath: string, modelConfig: ModelConfig): Promise<VisionResult> {
+    // OpenAI/Gemini Vision API を呼び出し
+    const result = await this.callVisionAPI(imagePath, modelConfig);
+    return {
+      text: result.text,
+      confidence: result.confidence,
+      layout: result.layout
+    };
+  }
+}
+```
+
+## 🚀 デプロイ設定
+
+### Docker Compose
+
+```yaml
+services:
+  libreoffice:
+    build:
+      context: ./libreoffice-server
+    ports:
+      - "8100:8100"
+    volumes:
+      - ./uploads:/uploads
+      - ./temp:/temp
+
+  server:
+    environment:
+      - LIBREOFFICE_URL=http://libreoffice:8100
+      - TEMP_DIR=/app/temp
+    depends_on:
+      - libreoffice
+```
+
+### 環境変数
+
+```env
+# LibreOffice サービス
+LIBREOFFICE_URL=http://127.0.0.1:8100
+
+# 一時ファイルディレクトリ
+TEMP_DIR=./temp
+
+# Vision API 設定
+VISION_API_KEY=sk-xxx
+VISION_MODEL=gpt-4-vision-preview
+```
+
+## 💰 コスト管理
+
+### 予想コスト
+
+| ドキュメント形式 | ページ数 | 予想コスト | 処理時間 |
+|---------|------|---------|---------|
+| PDF | 10ページ | $0.10 | 約 1分 |
+| Word | 50ページ | $0.50 | 約 5分 |
+| PPT | 30ページ | $0.30 | 約 3分 |
+
+### 節約戦略
+
+- 小規模ドキュメント (<10ページ): 高精度モードを使用。
+- 大規模ドキュメント (>50ページ): 分割して処理するか、高速モードを検討。
+- テキストのみのドキュメント: 常に高速モードを使用。
+
+## 🔧 利用方法
+
+### 1. サービスの起動
+
+```bash
+# すべてのサービスを起動
+docker-compose up -d
+
+# 状態の確認
+docker-compose ps
+```
+
+### 2. サービスの検証
+
+```bash
+# LibreOffice のヘルスチェック
+curl http://localhost:8100/health
+
+# API ドキュメントの確認
+open http://localhost:8100/docs
+
+# 変換テスト
+curl -X POST -F "file=@test.docx" http://localhost:8100/convert
+```
+
+### 3. Vision モデルの設定
+
+1. 「モデル管理」に移動します。
+2. Vision をサポートするモデル (GPT-4V/Gemini Pro Vision) を追加します。
+3. API キーを設定します。
+4. 「ビジョンをサポート」オプションにチェックを入れます。
+
+### 4. アップロードテスト
+
+1. PDF/Word/PPT ファイルを選択します。
+2. アップロード画面で「高精度モード」を選択します。
+3. 処理の進捗とコストの見積もりを確認します。
+
+## 🔍 トラブルシューティング
+
+### LibreOffice サービスの問題
+
+```bash
+# コンテナ状態の確認
+docker-compose ps libreoffice
+
+# ログを表示
+docker-compose logs libreoffice
+
+# サービスの再起動
+docker-compose restart libreoffice
+```
+
+### Vision 分析の失敗
+
+- API キーの設定を検証してください。
+- モデルが Vision をサポートしているか確認してください。
+- ネットワーク接続が正常か確認してください。
+- 詳細なエラーログを確認してください。
+
+### メモリ使用率が高すぎる場合
+
+- バッチ処理サイズを調整してください。
+- 同時処理数を制限してください。
+- メモリの使用状況を監視してください。
+
+## 📊 監視指標
+
+### 主要な指標
+
+- 変換成功率: >95%
+- 平均処理時間: <10分 / 100ページ
+- Vision 分析の精度: >85%
+- コスト管理: <$0.30 / ドキュメント
+
+### ログの確認
+
+```bash
+# リアルタイムログ
+docker-compose logs -f server | grep "Vision\|高精度モード"
+
+# LibreOffice ログ
+docker-compose logs -f libreoffice
+```
+
+## ⚡ クイックコマンド
+
+```bash
+# 一括起動
+docker-compose up -d
+
+# ヘルスチェック
+curl http://localhost:8100/health
+
+# API ドキュメントを表示
+open http://localhost:8100/docs
+
+# 変換テスト
+curl -X POST -F "file=@test.docx" http://localhost:8100/convert | jq
+
+# ログを表示
+docker-compose logs -f libreoffice server
+```
+
+## 🎯 技術選型の説明
+
+### なぜ FastAPI を選んだのか
+
+| 特徴 | Flask | FastAPI | 優位点 |
+|------|-------|---------|------|
+| パフォーマンス | 中程度 | ⭐⭐⭐⭐⭐ 非同期 | 2〜3倍高速 |
+| ドキュメント | 拡張が必要 | ⭐⭐⭐⭐⭐ 自動生成 | `/docs` で即座にアクセス可能 |
+| 型安全性 | オプション | ⭐⭐⭐⭐⭐ 強制的 | エラーの削減 |
+| 本番対応 | 設定が必要 | ⭐⭐⭐⭐⭐ 即利用可能 | 最小限の設定で運用可能 |
+
+### FastAPI の核となるメリット
+
+1. **自動ドキュメント**: <http://localhost:8100/docs> にて利用可能。
+2. **型安全性**: リクエストパラメータを自動的に検証。
+3. **非同期処理**: 複数のリクエストを同時に処理可能。
+4. **本番対応**: パフォーマンスの最適化が組み込まれている。
+
+---
+
+**更新日**: 2025-01-14
+**バージョン**: v2.0
+**ステータス**: 実装済み

+ 68 - 0
docs/design/feat-auto-title-generation.md

@@ -0,0 +1,68 @@
+# Feature Design: Automatic Title Generation (feat-auto-title-generation)
+
+## 1. Overview
+This feature automatically generates meaningful titles for uploaded documents and chat sessions using AI. It aims to replace generic filenames and "New Conversation" labels with content-aware titles, improving user experience and organization.
+
+## 2. Requirements
+
+### 2.1 Document Title Generation
+- **Trigger**: Automatically triggered after text extraction (Fast or Precise mode).
+- **Process**:
+    1. Extract a sample of the document content (first 2,000 - 3,000 characters).
+    2. Send the content to the default LLM with a specific generation prompt.
+    3. Update the `KnowledgeBase` record with the generated title.
+- **Rules**:
+    - The title should be concise (less than 50 characters).
+    - It should be in the user's preferred language (defaulting to the detected document language if possible).
+    - Output should be "raw" (no preamble like "The title is...").
+
+### 2.2 Chat Title Generation
+- **Trigger**: Triggered after the first user message and its corresponding assistant response are recorded.
+- **Process**:
+    1. Collect the initial message pair.
+    2. Send the pair to the default LLM with a generation prompt.
+    3. Update the `SearchHistory` record's `title` field.
+- **Rules**: Same as document titles.
+
+## 3. Technical Design
+
+### 3.1 Data Model Changes
+- **KnowledgeBase Entity**: Add a `title` field (nullable, optional). If empty, fallback to `originalName`.
+- **SearchHistory Entity**: No changes required (has `title`).
+
+### 3.2 Backend Implementation
+
+#### KnowledgeBaseService
+- Add `generateTitle(kbId: string)` method.
+- Hook into `processFile` after `updateStatus(kbId, FileStatus.EXTRACTED)`.
+
+#### ChatService / SearchHistoryService
+- Add logic to check if the session title is still the default (usually the first message snippet) and trigger `generateTitle(historyId: string)` after the first assistant response.
+
+#### Prompt Design
+- **Document Prompt**:
+  ```text
+  You are a document analyzer. Read the provided text and generate a concise, professional title (max 50 chars). 
+  Return ONLY the title.
+  Language: {userLanguage}
+  Text: {contentSample}
+  ```
+- **Chat Prompt**:
+  ```text
+  Based on the following conversation snippet, generate a short, descriptive title (max 50 chars) that summarizes the topic.
+  Return ONLY the title.
+  Language: {userLanguage}
+  Snippet:
+  User: {userMessage}
+  AI: {aiResponse}
+  ```
+
+## 4. Verification Plan
+
+### Automated Tests
+- Integration tests in `KnowledgeBaseService` to verify the title field is updated after processing.
+- Mock LLM responses to ensure the title update logic works.
+
+### Manual Verification
+- Upload various files (PDF, Word, TXT) and verify the displayed title in the knowledge base list.
+- Start a new chat, send a message, and check the sidebar for the updated session title.

+ 59 - 0
docs/design/feat-cross-doc-comparison.md

@@ -0,0 +1,59 @@
+# Design: Cross-Document Comparison (Agentic Workflow)
+
+## 1. Background & Problem
+Users often need to compare multiple documents (e.g., "Compare the financial reports of Q1 and Q2" or "Differences between Product A and Product B specs").
+Standard RAG retrieves chunks based on semantic similarity to the query. While "Multi-Query" helps, standard RAG might:
+1.  Retrieve too many chunks from one document and miss the other.
+2.  Fail to align comparable attributes (e.g., comparing "revenue" in Doc A with "profit" in Doc B).
+3.  Produce a generic text answer instead of a structured comparison.
+
+## 2. Solution: Agentic Comparison Workflow
+We will implement a specialized workflow (or "Light Agent") that:
+1.  **Analyzes the Request**: Identifies the subjects to compare (e.g., "Q1 Report", "Q2 Report") and the dimensions (e.g., "Revenue", "Risks").
+2.  **Targeted Retrieval**:
+    -   Explicitly filters/searches for Doc A.
+    -   Explicitly filters/searches for Doc B.
+3.  **Structured Synthesis**: Generates the answer, potentially forcing a Markdown Table format for clarity.
+
+## 3. Technical Architecture
+
+### 3.1 Backend (`ComparisonService` or extension to `RagService`)
+-   **Intent Detection**: Modify `ChatService` or `RagService` to detect comparison intent (can utilize LLM or simple heuristics + keywords).
+-   **Planning**: If comparison is detected:
+    1.  Identify Target Files: Resolve file names/IDs from the query (e.g., "Q1" -> matches file "2024_Q1_Report.pdf").
+    2.  Dimension Extraction: What to compare? (e.g., "summary", "key metrics").
+    3.  Execution:
+        -   Run Search on File A with query "key metrics".
+        -   Run Search on File B with query "key metrics".
+        -   Combine context.
+-   **Prompting**: Use a prompt optimized for comparison (e.g., "Generate a comparison table...").
+
+### 3.2 Frontend (`ChatInterface`)
+-   **UI Trigger**: (Optional) specific "Compare" button, or just natural language.
+-   **Visuals**: Render the response standard markdown (which supports tables).
+-   **Source Attribution**: Ensure citations map back to the correct respective documents.
+
+## 4. Implementation Steps
+
+1.  **Intent & Entity Extraction (Simple Version)**:
+    -   In `RagService`, add a step `detectComparisonIntent(query)`.
+    -   Return `subjects: string[]` (approximate filenames) and `dimensions: string`.
+    
+2.  **Targeted Search**:
+    -   Use `elasticsearchService` to search *specifically* within the resolved file IDs (if we can map names to IDs).
+    -   Fall back to broad search if file mapping fails.
+
+3.  **Comparison Prompt**:
+    -   Update `rag.service.ts` to use a `comparisonPromise` if intent is detected.
+
+## 5. Risks & limitations
+-   **File Name Matching**: Mapping user spoken "Q1" to "2024_Q1_Report_Final.pdf" is hard without fuzzy matching or LLM resolution.
+    -   *Mitigation*: Use a lightweight LLM call or fuzzy search on the file list to resolve IDs.
+-   **Latency**: Two searches + entity resolution might add latency.
+    -   *Mitigation*: Run searches in parallel.
+
+## 6. MVP Scope
+-   Automated detection of "Compare A and B".
+-   Attempt to identify if A and B refer to specific files in the selected knowledge base.
+-   If identified, restrict search scopes accordingly (or boost them).
+-   Generate a table response.

+ 37 - 0
docs/design/feat-highlight-jump.md

@@ -0,0 +1,37 @@
+# Feature Design: Highlight Jump (Precise Sourcing)
+
+## Problem Statement
+Currently, when a user clicks a citation in the chat, they can see the source text in a drawer or open the PDF. However, the PDF opens to the first page (or just the file) without pinpointing the exact location of the referenced information. This forces the user to manually search for the content.
+
+## Proposed Solution
+Implement "Highlight Jump" functionality:
+1.  **Page Jump**: When opening a citation, the PDF viewer should immediately jump to the specific page number containing the chunk.
+2.  **Text Highlighting**: The specific text segment used in the citation should be highlighted visually on the PDF page.
+
+## Technical Implementation
+
+### Frontend
+
+#### 1. `PDFPreview.tsx`
+-   **Enable Text Layer**: Currently, `PDFPreview` renders only to a `<canvas>`. We must enable `pdf.js` **Text Layer** rendering on top of the canvas. This allows text selection and searching.
+-   **New Props**:
+    -   `initialPage`: Already exists? Need to verify it works reliably.
+    -   `highlightText`: A string (the chunk content) to search for and highlight.
+-   **Highlight Logic**:
+    -   On page load, if `highlightText` is provided, search for this text in the Text Layer.
+    -   Apply a visual highlight (e.g., yellow background) to the matching DOM elements in the text layer.
+    -   Scroll the highlighted element into view.
+
+#### 2. `SourcePreviewDrawer.tsx`
+-   Pass the `pageNumber` and `content` (as `highlightText`) to the `onOpenFile` callback.
+-   Update the "Open File" button to trigger this with the correct metadata.
+
+#### 3. `ChatInterface.tsx` / `ChatView.tsx`
+-   Ensure the state that manages the open PDF preview receives the `pageNumber` and `highlightText` from the source.
+
+### Backend
+-   **No changes required** if `RagSearchResult` already contains `pageNumber`. (Verified: It does).
+
+## Limitations
+-   **OCR Files**: If the file was indexed via OCR (images), `pdf.js` might not extract a text layer that matches exactly what Tika extracted, or might have no text layer. In this case, we fallback to just Page Jump.
+-   **Text Mismatch**: If the chunk text is slightly different from the PDF text layer (due to cleaning/normalization during indexing), exact string matching might fail. We will try to match a substring or a fuzzy match if possible, but exact match of the first ~50 chars is a good starting point.

+ 52 - 0
docs/design/feat-query-expansion-hyde.md

@@ -0,0 +1,52 @@
+# Feature Design: Query Expansion & HyDE Integration
+
+This document outlines the design for improving search relevance in Lumina using Query Expansion (Multi-Query) and Hypothetical Document Embeddings (HyDE).
+
+## Problem Statement
+The current search implementation relies on the user's original query. Simple vector search can sometimes fail to match relevant documents due to:
+1.  **Keyword Mismatch**: The user might use different terminology than the document.
+2.  **Semantic Gap**: The query might be too brief to capture the full semantic context required for a good vector match.
+
+## Proposed Solution
+
+### 1. Query Expansion (Multi-Query)
+We will use an LLM to generate 3 unique variations of the user's query. This helps to:
+- Capture different facets of the user's intent.
+- Increase the probability of hitting relevant segments in the knowledge base.
+
+### 2. HyDE (Hypothetical Document Embeddings)
+We will use an LLM to generate a brief "hypothetical" answer to the user's query.
+- Instead of embedding the question, we embed the hypothetical answer.
+- This often results in better vector matches because we are comparing "answer-like" vectors with "document-like" segments.
+
+## Technical Implementation
+
+### Backend Changes
+
+#### `RagService` (server/src/rag/rag.service.ts)
+- **New Methods**:
+    - `expandQuery(query: string, userId: string): Promise<string[]>`: Generates 3 variations of the query.
+    - `generateHyDE(query: string, userId: string): Promise<string>`: Generates a hypothetical document.
+- **Update `searchKnowledge`**:
+    - Add `enableQueryExpansion` and `enableHyDE` parameters.
+    - Implement logic to handle multiple search requests (concurrently) and deduplicate results.
+
+#### `ChatService` (server/src/chat/chat.service.ts)
+- Pass the new search options from user settings or request parameters.
+
+### Frontend Changes
+
+#### `types.ts` (web/types.ts)
+- Update `AppSettings` to include `enableQueryExpansion` and `enableHyDE`.
+
+#### `SettingsDrawer.tsx`
+- Add UI toggles for these new search enhancement features.
+
+## Verification Plan
+
+### Backend Logs
+- Verify that LLM calls for expansion and HyDE are being made.
+- Log the generated queries and hypothetical documents for debugging.
+
+### Manual Verification
+- Compare search results with and without these features enabled for complex queries.

+ 32 - 0
docs/test_admin_features.md

@@ -0,0 +1,32 @@
+# Admin Feature Verification Test Cases
+
+## 1. User Management Access Control
+- [ ] Non-admin users should NOT see the "User Management" menu item
+- [ ] Admin users should see the "User Management" menu item
+- [ ] Non-admin users attempting to access user management should get a permission error
+- [ ] Admin users should be able to access user management successfully
+
+## 2. Admin User Password Modification
+- [ ] Admin users should see a "Change Password" button for each user in the user list
+- [ ] Clicking the button should open a password change modal
+- [ ] Admin users should be able to submit new passwords for other users
+- [ ] The password change should persist in the backend
+- [ ] Non-admin users should not have access to this functionality
+
+## 3. Knowledge Base Upload Restrictions
+- [ ] Non-admin users should NOT see the "Upload File" button in Knowledge Base View
+- [ ] Admin users should see the "Upload File" button in Knowledge Base View
+- [ ] Non-admin users attempting to upload directly via API should get a permission error
+- [ ] Admin users should be able to upload files successfully
+
+## 4. Knowledge Group Upload Restrictions
+- [ ] Non-admin users should NOT see the "Add File" or "Import Folder" buttons in Knowledge Group View
+- [ ] Admin users should see the "Add File" and "Import Folder" buttons in Knowledge Group View
+- [ ] Non-admin users attempting to upload via API should get a permission error
+- [ ] Admin users should be able to upload files to knowledge groups successfully
+
+## 5. Backend Security
+- [ ] Upload endpoints (POST /upload and POST /upload/text) should require AdminGuard
+- [ ] Import task endpoint (POST /import-tasks) should require AdminGuard
+- [ ] User update endpoint (PUT /users/:id) should accept password changes from admins
+- [ ] All existing functionality should remain operational for authorized users

+ 41 - 0
docs/testing/feat-auto-title-generation.md

@@ -0,0 +1,41 @@
+# 测试用例 - 标题自动生成 (Auto Title Generation)
+
+## 1. 功能概述
+本功能旨在提高系统的可用性,当用户上传文件或开始新的对话时,系统会自动调用 LLM 生成描述性的标题,而不是仅仅使用文件名或消息摘要。
+
+## 2. 测试场景与结果
+
+### 场景 A: 知识库文件标题生成
+- **测试步骤**:
+  1. 上传一个名为 `画面デザイン・機能案_20260209.pptx` 的文件。
+  2. 观察后台日志中是否调用了标题生成逻辑。
+  3. 检查数据库中该文件的 `title` 字段。
+- **验证结果**:
+  - **日志显示**: `[ChatService] Generated title for KnowledgeBase: 生成AIチャットボットUI改修案`
+  - **数据库确认**:
+    ```text
+    ID: e7841ec0-de0e-4e5e-afd8-aa987d872161
+    Name: 画面デザイン・機能案_20260209.pptx
+    Title: 生成AIチャットボットUI改修案
+    ```
+- **结论**: **通过**
+
+### 场景 B: 聊天会话标题生成
+- **测试步骤**:
+  1. 开启新对话,询问 "RAG アーキテクチャ 意味と仕組み"。
+  2. 等待对话结束。
+  3. 刷新历史列表,查看生成的标题。
+- **验证结果**:
+  - **日志显示**: `[ChatService] Generated title for chat 122c8c54-ffe6-4bf8-96d9-53bd0a3da631: RAG:検索増強生成の概要`
+  - **前端显示**: 历史列表中准确显示 "RAG:検索増強生成の概要"。
+- **结论**: **通过**
+
+## 3. 边缘情况测试
+- **LLM 调用失败**: 系统应回退使用文件名或消息前几个字符。
+- **网络延迟**: 标题生成应异步进行或不阻塞主响应流程。
+
+## 4. 最终状态
+- [x] 代码逻辑实现
+- [x] 数据库字段更新
+- [x] 后端日志验证
+- [x] 前端显示验证

+ 43 - 0
docs/testing/feat-query-expansion-hyde.md

@@ -0,0 +1,43 @@
+# 测试用例 - 检索增强 (Query Expansion & HyDE)
+
+## 1. 功能概述
+本功能通过“查询扩展(Query Expansion)”和“假设性文档嵌入(HyDE)”提升检索的相关性,特别是针对短查询或跨语言查询。
+
+## 2. 测试场景与结果
+
+### 场景 A: 查询扩展 (Query Expansion)
+- **测试步骤**:
+  1. 在设置中开启“查询扩展”。
+  2. 输入查询: "RAG アーキテクチャ 意味と仕組み"。
+  3. 查看后台日志中生成的扩展查询。
+- **验证结果**:
+  - **日志显示**: `[RagService] Generated query variations: RAGの基本的な定義と構成要素 | 検索増強生成の仕組みとメリット | LLMと外部知識の統合プロセス`
+  - **检索执行**: 系统针对这几个变体分别执行了 Elasticsearch 检索并成功合并结果。
+- **结论**: **通过**
+
+### 场景 B: HyDE (Hypothetical Document Embeddings)
+- **测试步骤**:
+  1. 在设置中开启 "HyDE"。
+  2. 输入同样的查询。
+  3. 查看日志中生成的假设性文档。
+- **验证结果**:
+  - **日志显示**: `[RagService] Generated HyDE document: RAG(Retrieval-Augmented Generation)は、大規模言語モデル...`
+  - **检索执行**: 系统基于生成的长段落执行了向量搜索,提升了语义匹配度。
+- **结论**: **通过**
+
+### 场景 C: 重排序 (Rerank) 与阈值问题
+- **测试步骤**:
+  1. 开启 Rerank。
+  2. 检查得分较低(如 0.528)的结果是否能成功返回。
+- **验证结果**:
+  - **初始版本**: 结果被 `similarityThreshold` (0.7) 过滤。
+  - **修复后**: 系统识别到使用了 Rerank,自动应用 `scoreThreshold` (0.5),结果成功返回。
+  - **日志显示**: `Results after filtering (threshold 0.5, usedRerank=true): 1 / 1 items`
+- **结论**: **通过**
+
+## 3. 最终状态
+- [x] 设置面板开关生效
+- [x] 多级检索逻辑正确
+- [x] 结果合并与消重逻辑
+- [x] Rerank 阈值解耦逻辑
+- [x] 前端国际化配置

+ 0 - 0
identifier.sqlite


+ 57 - 0
libreoffice-server/Dockerfile

@@ -0,0 +1,57 @@
+FROM python:3.12-alpine
+
+# 配置 APK standard repositories and install LibreOffice 及 dependencies
+RUN echo "https://dl-cdn.alpinelinux.org/alpine/v3.19/main" > /etc/apk/repositories && \
+    echo "https://dl-cdn.alpinelinux.org/alpine/v3.19/community" >> /etc/apk/repositories && \
+    apk update && \
+    apk add --no-cache \
+    libreoffice \
+    libreoffice-common \
+    libreoffice-writer \
+    libreoffice-impress \
+    libreoffice-calc \
+    font-noto \
+    font-noto-cjk \
+    ttf-dejavu \
+    imagemagick \
+    && rm -rf /var/cache/apk/*
+
+# 安装 Python 依赖
+COPY requirements.txt .
+RUN pip install --no-cache-dir -r requirements.txt
+
+# Install Node.js, Chromium, and essential dependencies for Puppeteer
+RUN apk add --no-cache \
+    nodejs \
+    npm \
+    chromium \
+    nss \
+    freetype \
+    harfbuzz \
+    ca-certificates \
+    ttf-dejavu \
+    && rm -rf /var/cache/apk/*
+
+# Configure Puppeteer to use installed Chromium
+ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
+    PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
+
+# Install Node dependencies
+COPY package.json .
+RUN npm install --registry=https://registry.npmmirror.com
+
+WORKDIR /app
+COPY main.py /app/
+COPY md_to_pdf.js /app/
+
+# Link node_modules to app directory so the script can find required modules
+RUN ln -sf /node_modules /app/node_modules
+
+# 创建挂载目录
+RUN mkdir -p /app/uploads /temp
+
+# 暴露端口
+EXPOSE 8100
+
+# 启动 FastAPI
+CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8100", "--workers", "2"]

+ 214 - 0
libreoffice-server/README.md

@@ -0,0 +1,214 @@
+# LibreOffice FastAPI ドキュメント変換サービス
+
+## 📋 概要
+
+これは FastAPI ベースの独立したドキュメント変換サービスで、Word、PPT、Excel などのドキュメントを PDF に変換するために使用されます。RAG の高精度モードにおけるドキュメント処理パイプラインをサポートします。
+
+## 🎯 コア機能
+
+- **形式変換**: Word/PPT/Excel → PDF
+- **PDF スルーパス**: PDF ファイルは変換せずにそのまま返却
+- **自動ドキュメント生成**: `/docs` にアクセスしてインタラクティブな API ドキュメントを確認可能
+- **ヘルスチェック**: `/health` エンドポイントによるサービス状態の監視
+- **非同期処理**: FastAPI の非同期アーキテクチャによる高性能な処理
+
+## 🚀 クイックスタート
+
+### ローカル開発
+
+```bash
+# 1. 依存関係のインストール
+pip install -r requirements.txt
+
+# 2. サービスの起動
+uvicorn main:app --reload --port 8100
+
+# 3. ドキュメントへのアクセス
+open http://localhost:8100/docs
+```
+
+### Docker デプロイ
+
+```bash
+# 1. イメージのビルド
+docker build -t libreoffice-server .
+
+# 2. コンテナの実行
+docker run -d \
+  --name lo-converter \
+  -p 8100:8100 \
+  -v ./uploads:/uploads \
+  -v ./temp:/temp \
+  libreoffice-server
+
+# 3. ヘルスチェック
+curl http://localhost:8100/health
+```
+
+## 📡 API エンドポイント
+
+### POST /convert
+
+ドキュメントを PDF に変換します。
+
+**リクエスト**:
+
+```bash
+curl -X POST -F "file=@test.docx" http://localhost:8100/convert
+```
+
+**レスポンス**:
+
+```json
+{
+  "pdf_path": "/uploads/test.pdf",
+  "converted": true,
+  "original": "test.docx",
+  "file_size": 102400
+}
+```
+
+**サポートされている形式**:
+
+- `.pdf` - そのまま返却
+- `.doc`, `.docx` - Word ドキュメント
+- `.ppt`, `.pptx` - PowerPoint プレゼンテーション
+- `.xls`, `.xlsx` - Excel スプレッドシート
+
+### GET /health
+
+ヘルスチェックを行います。
+
+**レスポンス**:
+
+```json
+{
+  "status": "healthy",
+  "service": "libreoffice-converter",
+  "version": "1.0.0",
+  "uptime": 123.45
+}
+```
+
+### GET /docs
+
+自動生成される API ドキュメント (Swagger UI) です。
+
+## 🐳 Docker Compose との統合
+
+メインプロジェクトの `docker-compose.yml` に以下を追加してください:
+
+```yaml
+services:
+  libreoffice:
+    build: ./libreoffice-server
+    container_name: lo-converter
+    volumes:
+      - ./uploads:/uploads
+      - ./temp:/temp
+    ports:
+      - "8100:8100"
+    restart: unless-stopped
+    deploy:
+      resources:
+        limits:
+          memory: 1G
+          cpus: '1.0'
+```
+
+## 🔧 環境変数
+
+サービス自体に特別な環境変数は必要ありませんが、ボリュームのマウントによって以下のディレクトリを構成できます:
+
+- `/uploads` - ドキュメントの保存ディレクトリ
+- `/temp` - 一時ファイルのディレクトリ
+
+## 📊 パフォーマンスの目安
+
+| ドキュメント形式 | ページ数 | 変換時間 | 備考 |
+|---------|------|---------|------|
+| Word | 50ページ | 約10秒 | 書式を保持 |
+| PPT | 50ページ | 約15秒 | 各ページを画像として処理 |
+| Excel | 10ページ | 約8秒 | 表として変換 |
+| PDF | 任意 | 約0秒 | 直接返却 |
+
+## 🛠️ デバッグのヒント
+
+### ログの確認
+
+```bash
+docker logs -f lo-converter
+```
+
+### 変換テスト
+
+```bash
+# テストファイルの準備
+echo "test" > test.docx
+
+# 変換テストの実行
+curl -X POST -F "file=@test.docx" http://localhost:8100/convert | jq
+```
+
+### コンテナ内でのデバッグ
+
+```bash
+docker exec -it lo-converter sh
+```
+
+## 🔗 依存関係の説明
+
+- **FastAPI**: モダンな Python Web フレームワーク
+- **Uvicorn**: ASGI サーバー
+- **LibreOffice**: ドキュメント変換エンジン
+- **Pydantic**: データバリデーション
+
+## 📝 注意事項
+
+1. **ファイルサイズ**: 100MB 以内に制限することを推奨します。
+2. **タイムアウト**: デフォルトは 300 秒です。必要に応じてコード内で調整してください。
+3. **並列処理**: 2〜3 個のワーカーを推奨します。
+4. **メモリ制限**: 1GB を推奨します。
+5. **一時ファイル**: 定期的なクリーンアップが必要です。
+
+## 🎯 メインシステムとの連携
+
+### サーバー側の呼び出し例
+
+```typescript
+// server/src/libreoffice/libreoffice.service.ts
+async convertToPDF(filePath: string): Promise<string> {
+  const fileName = path.basename(filePath);
+  const fileBuffer = await fs.readFile(filePath);
+
+  const formData = new FormData();
+  formData.append('file', fileBuffer, fileName);
+
+  const response = await axios.post(
+    `${this.baseUrl}/convert`,
+    formData,
+    { timeout: 300000 }
+  );
+
+  return response.data.pdf_path;
+}
+```
+
+## 📚 関連ドキュメント
+
+- [メインプロジェクト README](../README.md)
+- [Vision Pipeline の設計](../docs/VISION_PIPELINE_COMPLETE.md)
+- [デプロイガイド](../docs/DEPLOYMENT.md)
+
+## 🚨 故障診断
+
+| 問題 | 原因 | 解決策 |
+|------|------|---------|
+| 変換失敗 | LibreOffice がインストールされていない | Dockerfile の依存関係を確認してください |
+| タイムアウト | ファイルが大きすぎる | タイムアウト時間を増やすか、ファイルを分割してください |
+| ポート競合 | 8100 ポートが既に使用されている | ポートマッピングを変更してください |
+| 権限エラー | ディレクトリの権限不足 | ボリュームの権限を確認してください |
+
+## 📄 ライセンス
+
+MIT License

+ 190 - 0
libreoffice-server/main.py

@@ -0,0 +1,190 @@
+from fastapi import FastAPI, File, UploadFile, HTTPException
+from fastapi.responses import RedirectResponse, FileResponse
+from pydantic import BaseModel
+from typing import Optional
+import subprocess
+import os
+import time
+from PIL import Image  # Pillowライブラリを追加
+import io
+
+# レスポンスモデル
+class ConvertResponse(BaseModel):
+    pdf_path: str
+    converted: bool
+    original: Optional[str] = None
+    file_size: Optional[int] = None
+    error: Optional[str] = None
+
+class HealthResponse(BaseModel):
+    status: str
+    service: str
+    version: str
+    uptime: float
+
+# FastAPI アプリケーション
+app = FastAPI(
+    title="LibreOffice ドキュメント変換サービス",
+    description="Word/PPT/Excel/PDF を PDF に変換し、混合内容のドキュメント処理をサポートします",
+    version="1.0.0",
+    docs_url="/docs",
+    redoc_url="/redoc"
+)
+
+start_time = time.time()
+
+@app.get("/", include_in_schema=False)
+async def root():
+    """ドキュメントページへリダイレクト"""
+    return RedirectResponse(url="/docs")
+
+@app.get("/health", response_model=HealthResponse)
+async def health():
+    """ヘルスチェックインターフェース"""
+    return HealthResponse(
+        status="healthy",
+        service="libreoffice-converter",
+        version="1.0.0",
+        uptime=time.time() - start_time
+    )
+
+@app.post("/convert")
+async def convert(file: UploadFile = File(...)):
+    """
+    ドキュメント変換インターフェース
+    戻り値: PDF ファイルストリーム
+    """
+    try:
+        # ファイル形式の検証
+        allowed_extensions = [
+            '.pdf', '.doc', '.docx', '.ppt', '.pptx', '.xls', '.xlsx',
+            '.md', '.txt', '.rtf', '.odt', '.ods', '.odp',
+            '.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp'
+        ]
+        file_ext = os.path.splitext(file.filename)[1].lower()
+
+        if file_ext not in allowed_extensions:
+            raise HTTPException(
+                status_code=400,
+                detail=f"サポートされていないファイル形式です: {file_ext}。サポート対象: {', '.join(allowed_extensions)}"
+            )
+
+        # uploads ディレクトリの存在を確認
+        upload_dir = "/app/uploads" if os.path.exists("/app/uploads") else "./uploads"
+        os.makedirs(upload_dir, exist_ok=True)
+
+        # アップロードファイルの保存
+        filepath = os.path.join(upload_dir, file.filename)
+        with open(filepath, "wb") as buffer:
+            content = await file.read()
+            buffer.write(content)
+
+        # PDF の場合はそのまま返却
+        if file_ext == '.pdf':
+            return FileResponse(filepath, filename=file.filename, media_type='application/pdf')
+
+        if file_ext == '.md':
+            # Node.js スクリプトを使用して Markdown を PDF にレンダリング
+            expected_pdf = filepath.rsplit('.', 1)[0] + '.pdf'
+            cmd = [
+                'node',
+                '/app/md_to_pdf.js',
+                filepath,
+                expected_pdf
+            ]
+        elif file_ext in ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp']:
+            # 画像ファイルの場合は Pillow を使用して PDF に変換
+            expected_pdf = filepath.rsplit('.', 1)[0] + '.pdf'
+
+            # 画像を開いてPDFとして保存
+            with Image.open(filepath) as img:
+                # RGBAモードの場合はRGBに変換(透明度がある画像対応)
+                if img.mode in ('RGBA', 'LA', 'P'):
+                    # 白い背景に変換
+                    background = Image.new('RGB', img.size, (255, 255, 255))
+                    if img.mode == 'P':
+                        img = img.convert('RGBA')
+                    background.paste(img, mask=img.split()[-1] if img.mode in ('RGBA', 'LA') else None)
+                    img = background
+                elif img.mode != 'RGB':
+                    img = img.convert('RGB')
+
+                # PDFとして保存
+                img.save(expected_pdf, 'PDF', resolution=100.0, save_all=False)
+
+            # PDF生成が完了したことを確認
+            if not os.path.exists(expected_pdf):
+                raise HTTPException(
+                    status_code=500,
+                    detail="画像からPDFへの変換は成功しましたが、出力ファイルが見つかりません"
+                )
+            
+            # 画像変換完了、PDFファイルを返却
+            filename_base = os.path.splitext(file.filename)[0]
+            return FileResponse(expected_pdf, filename=f"{filename_base}.pdf", media_type='application/pdf')
+        else:
+            # LibreOffice による変換
+            cmd = [
+                'soffice',
+                '--headless',
+                '--convert-to', 'pdf',
+                '--outdir', upload_dir,
+                filepath
+            ]
+
+        result = subprocess.run(
+            cmd,
+            capture_output=True,
+            text=True,
+            timeout=600,  # 複雑なMarkdown変換をサポートするために10分に延長
+        )
+
+        # Combine stdout and stderr for error reporting since capture_output uses PIPE
+        combined_output = result.stdout if result.stdout else ""
+        if result.stderr:
+            combined_output += "\n" + result.stderr
+
+        # Node.jsスクリプトの実際の出力を表示して、デバッグ
+        print(f"Node.js script output: {combined_output}")
+
+        if result.returncode != 0:
+            print(f"Subprocess failed with return code: {result.returncode}")
+
+            # Combine stdout and stderr for error reporting
+            combined_output = result.stdout if result.stdout else ""
+            if result.stderr:
+                combined_output += "\n" + result.stderr
+
+            print(f"Subprocess output: {combined_output}")
+            raise HTTPException(
+                status_code=500,
+                detail=f"変換に失敗しました: {combined_output}"
+            )
+
+        # 出力ファイルの確認
+        expected_pdf = filepath.rsplit('.', 1)[0] + '.pdf'
+        if not os.path.exists(expected_pdf):
+            raise HTTPException(
+                status_code=500,
+                detail="変換は成功しましたが、出力ファイルが見つかりません"
+            )
+
+        filename_base = os.path.splitext(file.filename)[0]
+        return FileResponse(expected_pdf, filename=f"{filename_base}.pdf", media_type='application/pdf')
+
+    except HTTPException:
+        raise
+    except subprocess.TimeoutExpired:
+        raise HTTPException(status_code=504, detail="変換タイムアウト (300秒)")
+    except Exception as e:
+        raise HTTPException(status_code=500, detail=str(e))
+
+@app.get("/version")
+async def version():
+    """バージョン情報"""
+    return {
+        "service": "libreoffice-converter",
+        "version": "1.0.0",
+        "framework": "FastAPI",
+        "libreoffice": "7.x"
+    }

+ 498 - 0
libreoffice-server/md_to_pdf.js

@@ -0,0 +1,498 @@
+const fs = require('fs');
+const { execSync } = require('child_process');
+const path = require('path');
+const puppeteer = require('puppeteer');
+
+console.log('=== MD to PDF Converter Starting ===');
+console.log('Node.js version:', process.version);
+console.log('Working directory:', process.cwd());
+console.log('Input path:', process.argv[2]);
+console.log('Output path:', process.argv[3]);
+
+// Arguments: node md_to_pdf.js <input_md_path> <output_pdf_path>
+const inputPath = process.argv[2];
+const outputPath = process.argv[3];
+
+if (!inputPath || !outputPath) {
+    console.error('Usage: node md_to_pdf.js <input_md_path> <output_pdf_path>');
+    process.exit(1);
+}
+
+console.log(`Processing Markdown: ${inputPath}`);
+
+(async () => {
+    try {
+        console.log('Reading input file...');
+        let mdContent = fs.readFileSync(inputPath, 'utf8');
+        console.log(`File read successfully, length: ${mdContent.length} characters`);
+
+        // 1. Protect Math Blocks
+        const mathBlocks = [];
+        const placeholderPrefix = 'MATHBLOCK_PLACEHOLDER_';
+
+        mdContent = mdContent.replace(/\$\$([\s\S]*?)\$\$/g, (match, p1) => {
+            const id = mathBlocks.length;
+            mathBlocks.push(`$$${p1}$$`);
+            return `${placeholderPrefix}${id}`;
+        });
+
+        mdContent = mdContent.replace(/\$([^\$\n]+?)\$/g, (match, p1) => {
+            const id = mathBlocks.length;
+            mathBlocks.push(`$${p1}$`);
+            return `${placeholderPrefix}${id}`;
+        });
+        console.log(`Protected ${mathBlocks.length} math blocks`);
+
+        // 2. Convert to HTML using marked (CLI via npx or library?)
+        // Since we are in a container, we should use the library directly if possible,
+        // but the reference uses npx. To avoid npx/network dependency at runtime,
+        // we will require 'marked' from node_modules (assuming we verify it's installed).
+        const marked = require('marked');
+        console.log('Parsing markdown content...');
+        let finalHtml = marked.parse(mdContent);
+        console.log('Markdown parsed successfully');
+
+        // 3. Restore Math Blocks
+        mathBlocks.forEach((block, index) => {
+            finalHtml = finalHtml.replace(`${placeholderPrefix}${index}`, block);
+        });
+
+        // 4. Fix Mermaid syntax
+        finalHtml = finalHtml.replace(
+            /<pre><code class="language-mermaid">([\s\S]*?)<\/code><\/pre>/g,
+            (match, content) => {
+                content = content.replace(/&quot;/g, '"')
+                    .replace(/&#39;/g, "'")
+                    .replace(/&gt;/g, '>')
+                    .replace(/&lt;/g, '<')
+                    .replace(/&amp;/g, '&');
+                return `<div class="mermaid">${content}</div>`;
+            }
+        );
+
+        // 5. Wrap in Template
+        const template = `
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <title>Document</title>
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown-light.min.css">
+
+    <!-- Mermaid -->
+    <script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
+
+    <!-- MathJax -->
+    <script>
+    window.MathJax = {
+      tex: {
+        inlineMath: [['$', '$'], ['\\\\(', '\\\\)']],
+        displayMath: [['$$', '$$'], ['\\\\[', '\\\\]']],
+        processEscapes: false
+      },
+      startup: {
+        pageReady: () => {
+            return MathJax.startup.defaultPageReady().then(() => {
+                const div = document.createElement('div');
+                div.id = 'mathjax-finished';
+                div.style.display = 'none';
+                document.body.appendChild(div);
+            });
+        }
+      }
+    };
+    </script>
+    <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
+
+    <style>
+        body {
+            box-sizing: border-box;
+            margin: 0 auto;
+            padding: 20px;
+        }
+        .mermaid {
+            display: flex;
+            justify-content: center;
+            margin: 20px 0;
+        }
+        table {
+            width: 100% !important;
+            display: table !important;
+        }
+    </style>
+
+    <!-- Embedded Mermaid Library -->
+    <script>
+        // This is a minimal stub to prevent errors when mermaid is referenced but not available
+        if (typeof window.mermaid === 'undefined') {
+            window.mermaid = {
+                initialize: function() {},
+                init: function() {},
+                render: function() {}
+            };
+        }
+    </script>
+
+    <!-- MathJax configuration and library -->
+    <script>
+        window.MathJax = {
+          tex: {
+            inlineMath: [['$', '$'], ['\\\\(', '\\\\)']],
+            displayMath: [['$$', '$$'], ['\\\\[', '\\\\]']],
+            processEscapes: false
+          },
+          startup: {
+            pageReady: () => {
+                return MathJax.startup.defaultPageReady().then(() => {
+                    const div = document.createElement('div');
+                    div.id = 'mathjax-finished';
+                    div.style.display = 'none';
+                    document.body.appendChild(div);
+                });
+            }
+          }
+        };
+    </script>
+    <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
+</head>
+<body class="markdown-body">
+${finalHtml}
+<script>
+    // Initialize mermaid if it's available
+    if (typeof mermaid !== 'undefined') {
+        mermaid.initialize({ startOnLoad: true, theme: 'default', securityLevel: 'loose' });
+    } else {
+        console.log('Mermaid library not loaded, skipping initialization');
+    }
+</script>
+</body>
+</html>`;
+
+        console.log('Template prepared, starting PDF generation...');
+
+        // 6. Generate PDF with Puppeteer
+        console.log('Starting Puppeteer browser launch...');
+        const browser = await puppeteer.launch({
+            executablePath: '/usr/bin/chromium-browser', // Alpine location
+            args: [
+                '--no-sandbox',
+                '--disable-setuid-sandbox',
+                '--disable-dev-shm-usage',
+                '--disable-background-timer-throttling',
+                '--disable-renderer-backgrounding',
+                '--disable-backgrounding-occluded-windows',
+                '--memory-pressure-off',
+                '--js-flags=--max-old-space-size=4096',  // 增加内存限制
+                '--enable-features=NetworkService',
+                '--disable-features=VizDisplayCompositor',
+                '--disable-gpu',
+                '--disable-web-security',
+                '--disable-features=VizDisplayCompositor'
+            ],
+            headless: 'new',
+            timeout: 120000  // Increased timeout for containerized environment
+        });
+        console.log('Browser launched successfully');
+
+        const page = await browser.newPage();
+        console.log('Page created successfully');
+
+        // ページのビューポートとユーザーエージェントを設定
+        await page.setViewport({ width: 1200, height: 800 });
+        await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
+        console.log('Viewport and user agent set');
+
+        // さまざまなタイムアウトを設定 - 長時間の待機を避けるためにデフォルト値を低下
+        await page.setDefaultNavigationTimeout(30000); // 30秒
+        await page.setDefaultTimeout(30000); // 30秒
+        console.log('Timeouts configured');
+
+        // すべての外部リソースの読み込みをブロックするリクエストをインターセプト
+        await page.setRequestInterception(true);
+        page.on('request', (req) => {
+            // すべての外部リソース要求を完全にブロック(CDNリソースを含む)してネットワークタイムアウトを回避
+            const url = req.url();
+
+            if (url.startsWith('http') || url.startsWith('https') || url.startsWith('ftp')) {
+                // すべての外部リクエストに空白のレスポンスを返して、ネットワークタイムアウトエラーを回避
+                req.respond({
+                    status: 200,
+                    contentType: 'text/plain',
+                    body: ''
+                }).catch(() => {});
+            } else {
+                // ローカルおよびdata URLリソースを許可
+                req.continue().catch(() => {});
+            }
+        });
+        console.log('Request interception configured to block all external resources');
+
+        // エラーイベントを監視
+        page.on('error', (error) => {
+            console.error('Page error:', error);
+        });
+
+        page.on('pageerror', (error) => {
+            console.error('Page error event:', error);
+        });
+
+        page.on('console', (msg) => {
+            console.log('Browser console:', msg.text());
+        });
+
+        console.log('Error listeners attached');
+
+        // 再試行メカニズム
+        let success = false;
+        let attempts = 0;
+        const maxAttempts = 3;
+
+        while (!success && attempts < maxAttempts) {
+            attempts++;
+            console.log(`Attempt ${attempts} of ${maxAttempts} for PDF generation...`);
+            console.log(`HTML template length: ${template.length} characters`);
+
+            try {
+                console.log('About to navigate to data URL...');
+                // 外部リソースを待たずに高速なナビゲーションオプションを使用
+                await page.goto(`data:text/html;charset=UTF-8,${encodeURIComponent(template)}`, {
+                    waitUntil: 'domcontentloaded',  // 等待DOM加载完成,但不等待资源
+                    timeout: 30000  // Reduced timeout for faster failure
+                });
+                console.log('Page loaded successfully');
+
+                // 画像の読み込みを待機(タイムアウトあり、読み込み失敗の画像は素早くスキップ)
+                try {
+                    console.log('Checking for images to load...');
+                    await page.evaluate(async () => {
+                        const images = Array.from(document.querySelectorAll('img'));
+                        console.log(`Found ${images.length} images on the page`);
+                        if (images.length > 0) {
+                            // すべての画像の読み込みを待つのではなく、短時間だけ待って次に進む
+                            await new Promise((resolve) => {
+                                setTimeout(() => {
+                                    console.log(`Continuing after attempting to load ${images.length} images`);
+                                    resolve();
+                                }, 500); // 只等待500ms,不管图像是否加载完成
+                            });
+                        }
+                    });
+                } catch (e) {
+                    console.warn('Error checking images:', e.message);
+                }
+
+                // MathJaxのレンダリングを待機(タイムアウトあり)
+                console.log('Checking for MathJax...');
+                let mathjaxFinished = false;
+                let mermaidProcessed = false;  // 移动变量声明到这里
+                try {
+                    // ページに数式が含まれているか確認(MathJaxは通常、$...$または$$...$$形式の数式を処理します)
+                    const hasMathContent = await page.evaluate(() => {
+                        const html = document.documentElement.innerHTML;
+                        // 数学記号のタグを確認
+                        return html.includes('$') || html.includes('\\(') || html.includes('\\[') ||
+                               html.includes('\\begin{') || html.includes('math-tex') ||
+                               document.querySelectorAll('mjx-container').length > 0 ||
+                               document.querySelectorAll('[class*="math"]').length > 0;
+                    });
+
+                    console.log(`Math content found: ${hasMathContent}`);
+
+                    if (hasMathContent) {
+                        console.log('Math content detected, waiting for MathJax...');
+
+                        // 特定のセレクタを無限に待つのではなく、MathJaxの初期化に合理的な時間を待機
+                        await new Promise(r => setTimeout(r, 1000)); // 短暂等待1秒
+
+                        // MathJaxが存在するか再度確認
+                        const mathjaxExists = await page.evaluate(() => typeof window.MathJax !== 'undefined');
+
+                        if (mathjaxExists) {
+                            // MathJaxが存在する場合、レンダリング完了を待機
+                            await page.evaluate(async () => {
+                                if (window.MathJax && window.MathJax.Hub) {
+                                    await window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub]);
+                                } else if (window.MathJax && window.MathJax.typesetPromise) {
+                                    await window.MathJax.typesetPromise();
+                                }
+                            });
+
+                            console.log('MathJax typesetting completed');
+                            mathjaxFinished = true;
+                        } else {
+                            console.log('MathJax not found after content check');
+                        }
+                    } else {
+                        console.log('No math content found, skipping MathJax wait');
+                    }
+                } catch (e) {
+                    console.warn('Error checking MathJax:', e.message);
+                }
+
+                // MathJaxが完了していない場合、追加の時間を待機
+                if (!mathjaxFinished) {
+                    console.log('Waiting 1 second before generating PDF...');
+                    await new Promise(r => setTimeout(r, 1000));
+                }
+
+                // Mermaidが完了していない場合、追加の時間を待機
+                if (!mermaidProcessed) {
+                    console.log('Waiting 1 second before generating PDF...');
+                    await new Promise(r => setTimeout(r, 1000));
+                }
+
+                // 等待 Mermaid 图表渲染
+                console.log('Checking for Mermaid diagrams...');
+                try {
+                    // ページにMermaidチャートコンテナがあるか確認
+                    const mermaidElementsCount = await page.evaluate(() => document.querySelectorAll('.mermaid').length);
+                    console.log(`Mermaid diagrams found: ${mermaidElementsCount > 0}`);
+
+                    if (mermaidElementsCount > 0) {
+                        console.log(`Processing ${mermaidElementsCount} Mermaid diagrams...`);
+
+                        // Mermaidライブラリが存在するか確認し、初期化を試みる
+                        const mermaidExists = await page.evaluate(() => typeof mermaid !== 'undefined');
+
+                        if (mermaidExists) {
+                            console.log('Mermaid library found, attempting to initialize...');
+
+                            await page.evaluate(async () => {
+                                // mermaidオブジェクトが存在するか確認
+                                if (typeof mermaid !== 'undefined' && mermaid.init) {
+                                    try {
+                                        // Mermaidチャートの初期化を試みる
+                                        mermaid.init(undefined, '.mermaid');
+                                    } catch (e) {
+                                        console.log('Mermaid init error:', e.message);
+                                    }
+                                } else {
+                                    console.log('Mermaid library not fully loaded, skipping initialization');
+                                }
+
+                                // レンダリング完了を待機(最大5秒)
+                                const startTime = Date.now();
+                                while (Date.now() - startTime < 5000) {
+                                    // 未完成のMermaidチャートがあるか確認
+                                    const incompleteCharts = document.querySelectorAll('.mermaid:not(.mermaid-loaded)');
+                                    if (incompleteCharts.length === 0) {
+                                        break;
+                                    }
+                                    // 等待一小段时间后重试
+                                    await new Promise(r => setTimeout(r, 100));
+                                }
+                            });
+                        } else {
+                            console.log('Mermaid library not found in document, skipping processing');
+                        }
+
+                        console.log('Mermaid diagrams processed');
+                        mermaidProcessed = true;
+                    } else {
+                        console.log('No Mermaid diagrams found, skipping wait');
+                    }
+                } catch (e) {
+                    console.warn('Error processing Mermaid:', e.message);
+                }
+
+                // 等待页面基本渲染完成(不等待所有外部资源)
+                console.log('Waiting for basic page content to be loaded...');
+                try {
+                    // complete状態ではなくDOMContentLoadedイベントを待機
+                    await page.waitForFunction(() => document.readyState !== 'loading', { timeout: 10000 });  // Reduced timeout
+                    console.log('Page DOM loaded, readyState is not loading');
+                } catch (e) {
+                    console.warn('DOM did not finish loading, continuing...', e.message);
+                }
+
+                // 确保所有异步操作完成后再生成PDF
+                console.log('Waiting 2 seconds before generating PDF...');
+                await new Promise(r => setTimeout(r, 2000));
+
+                console.log('Generating PDF file...');
+                await page.pdf({
+                    path: outputPath,
+                    format: 'A4',
+                    printBackground: true,
+                    scale: 0.75, // Scale down to fit more content
+                    margin: { top: '10mm', right: '10mm', bottom: '10mm', left: '10mm' },
+                    timeout: 120000
+                });
+                console.log('PDF generated successfully');
+
+                success = true;
+                console.log(`PDF successfully generated at ${outputPath}`);
+
+            } catch (error) {
+                console.error(`Attempt ${attempts} failed:`, error.message);
+                console.error(`Error stack:`, error.stack);
+
+                // 致命的なエラーの場合は再試行不要
+                if (error.message.includes('Protocol error') ||
+                    error.message.includes('Target closed') ||
+                    error.message.includes('Browser closed') ||
+                    error.message.includes('Connection closed') ||
+                    error.message.includes('Navigation failed') ||
+                    error.message.includes('net::ERR_CONNECTION_CLOSED')) {
+                    console.error('Fatal browser error occurred, aborting retries');
+                    throw error;
+                }
+
+                if (attempts >= maxAttempts) {
+                    // すべての再試行が失敗した場合、最も簡略化された方法を試す
+                    console.log('All attempts failed, trying most basic PDF generation...');
+                    console.log('Creating a new page for basic method...');
+
+                    // 重新创建页面以确保干净的状态
+                    const basicPage = await browser.newPage();
+                    await basicPage.setViewport({ width: 1200, height: 800 });
+                    await basicPage.setDefaultNavigationTimeout(60000);
+
+                    await basicPage.goto(`data:text/html;charset=UTF-8,${encodeURIComponent(template)}`, {
+                        waitUntil: 'domcontentloaded',
+                        timeout: 120000  // Increased timeout for containerized environment
+                    });
+
+                    // 等待一段较短的时间
+                    console.log('Waiting 2 seconds in basic method...');
+                    await new Promise(r => setTimeout(r, 2000));
+
+                    try {
+                        console.log('Generating PDF with basic method...');
+                        await basicPage.pdf({
+                            path: outputPath,
+                            format: 'A4',
+                            printBackground: true,
+                            scale: 0.75,
+                            margin: { top: '10mm', right: '10mm', bottom: '10mm', left: '10mm' },
+                            timeout: 300000  // Increased timeout for containerized environment
+                        });
+                        success = true;
+                        console.log(`PDF generated using basic method at ${outputPath}`);
+                        await basicPage.close();
+                    } catch (basicError) {
+                        console.error('Basic PDF generation also failed:', basicError.message);
+                        console.error('Basic error stack:', basicError.stack);
+                        await basicPage.close();
+                        throw basicError;
+                    }
+                } else {
+                    // 一定時間待機してから再試行(システムが復旧する時間を与える)
+                    const delay = 10000 * attempts; // 逐次的に遅延時間を増加
+                    console.log(`Waiting ${delay}ms before retry...`);
+                    await new Promise(r => setTimeout(r, delay));
+                }
+            }
+        }
+
+        console.log('Closing browser...');
+        await browser.close();
+        console.log('Browser closed');
+        console.log('=== MD to PDF Conversion Completed Successfully ===');
+
+    } catch (err) {
+        console.error('Error during conversion:', err);
+        console.error('Error stack:', err.stack);
+        process.exit(1);
+    }
+})();

+ 8 - 0
libreoffice-server/package.json

@@ -0,0 +1,8 @@
+{
+    "name": "lumina-pdf-service",
+    "version": "1.0.0",
+    "dependencies": {
+        "marked": "^11.1.1",
+        "puppeteer": "^21.7.0"
+    }
+}

+ 5 - 0
libreoffice-server/requirements.txt

@@ -0,0 +1,5 @@
+fastapi==0.104.1
+uvicorn[standard]==0.24.0
+python-multipart==0.0.6
+pydantic==2.5.0
+Pillow==10.1.0

+ 68 - 0
nginx/conf.d/kb.conf

@@ -0,0 +1,68 @@
+# HTTP 重定向到 HTTPS
+server {
+    listen 80;
+    server_name localhost;
+    return 301 https://$server_name$request_uri;
+}
+
+# HTTPS 服务器
+server {
+    listen 443 ssl http2;
+    server_name localhost;
+
+    # SSL 配置
+    ssl_certificate /etc/nginx/conf.d/ssl/cert.pem;
+    ssl_certificate_key /etc/nginx/conf.d/ssl/key.pem;
+    ssl_protocols TLSv1.2 TLSv1.3;
+    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
+    ssl_prefer_server_ciphers off;
+
+    # 安全头
+    add_header X-Frame-Options DENY;
+    add_header X-Content-Type-Options nosniff;
+    add_header X-XSS-Protection "1; mode=block";
+    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
+
+    # 前端静态文件
+    location / {
+        root /usr/share/nginx/html;
+        index index.html;
+        try_files $uri $uri/ /index.html;
+
+        # 静态资源缓存
+        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
+            expires 1y;
+            add_header Cache-Control "public, immutable";
+        }
+    }
+
+    # API 代理到后端
+    location /api/ {
+        proxy_pass http://server:3001;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        client_max_body_size 100M;
+
+        # 大模型超时配置
+        proxy_connect_timeout 300s;
+        proxy_send_timeout 300s;
+        proxy_read_timeout 300s;
+    }
+
+    # 文件上传代理
+    location /uploads/ {
+        proxy_pass http://server:3001;
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+        client_max_body_size 100M;
+
+        # 上传超时配置
+        proxy_connect_timeout 60s;
+        proxy_send_timeout 300s;
+        proxy_read_timeout 300s;
+    }
+}

+ 22 - 0
nginx/conf.d/ssl/cert.pem

@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDkTCCAnmgAwIBAgIULBuv66QB9N/vpEjbbm09JBk/2hwwDQYJKoZIhvcNAQEL
+BQAwWDELMAkGA1UEBhMCQ04xEDAOBgNVBAgMB0JlaWppbmcxEDAOBgNVBAcMB0Jl
+aWppbmcxETAPBgNVBAoMCFNpbXBsZUtCMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcN
+MjUxMjE3MDk1OTEwWhcNMjYxMjE3MDk1OTEwWjBYMQswCQYDVQQGEwJDTjEQMA4G
+A1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzERMA8GA1UECgwIU2ltcGxl
+S0IxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAMP89ErAdnRKBEDHjWZmGdsAsYYQ4zipCvhyUaw1lQoXcDHUlIcSDmSw
+7oxFd8N+bcbvLA/9ADm5aQq0DzdYtbZhCRtqa0inVdROA231D9bGJGtJISicGze5
+wmV1fVmFbdsFuHvGLYeUaRoHsbLVjnRjHiOiAi5Ne7X92EeZrsQDeU8/oxQHgFcE
+UPMpb1U+cRZ2Dtr/1EGT0KkVj9qfL8Oxkdn+bfHtp5lqcXKHVEVi8pD51Ta2OsgT
+gTE88Czs3eqNheO0kgGx5SCn4xQ6fPR4aOrmt7RsvkBkfMn3MNV2VO5aucPv6fWH
+4hhn7kVU5aVSx4dOqxp1jEh+xeVVU7cCAwEAAaNTMFEwHQYDVR0OBBYEFFcsTq95
+70NiO1GvUImpyrmxXsvnMB8GA1UdIwQYMBaAFFcsTq9570NiO1GvUImpyrmxXsvn
+MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEVb8okxIwb36FED
++eWSetT7MPfyyXyMSmUXfH+fr+qJ1NkaLwIwOH3AUpvxqx5+zAx61McF5dZy/xkD
+Cms47sic2rbE04ALbAdkBCowuqs5ZzVF+YhWzRMXEkARxz3x+Kz+Elxfwg/9pQgG
+JCQvHtYCqrG5XbM8wHBaoSfIfoY5exxLNuevVBqytkKi4qlC/bHuG4CuI7g18N2x
+ozQGCP3C5F6l80EozaiYPZuNbR0mV6FeL7Lmtkoa1fl02svrKwtv9MX6oaXiAnIF
+MTxcNK7+QeUdvJLZSv8vWTrbKIIcRkOrcwOrotGt2zfj/RplcrXsayJ1HTViDQ9U
+yI6Tu74=
+-----END CERTIFICATE-----

+ 28 - 0
nginx/conf.d/ssl/key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDD/PRKwHZ0SgRA
+x41mZhnbALGGEOM4qQr4clGsNZUKF3Ax1JSHEg5ksO6MRXfDfm3G7ywP/QA5uWkK
+tA83WLW2YQkbamtIp1XUTgNt9Q/WxiRrSSEonBs3ucJldX1ZhW3bBbh7xi2HlGka
+B7Gy1Y50Yx4jogIuTXu1/dhHma7EA3lPP6MUB4BXBFDzKW9VPnEWdg7a/9RBk9Cp
+FY/any/DsZHZ/m3x7aeZanFyh1RFYvKQ+dU2tjrIE4ExPPAs7N3qjYXjtJIBseUg
+p+MUOnz0eGjq5re0bL5AZHzJ9zDVdlTuWrnD7+n1h+IYZ+5FVOWlUseHTqsadYxI
+fsXlVVO3AgMBAAECggEAG9GvgV7RUY9iDCnnJcZPXDk8eZmzDwtbncloU2flqGGM
+UN5qWMPU3DELI0kHB25OOcMgP4K7gfYR9W16jXIflOwwJT6VTOJHuhN6xCRZY7SL
+XdkrBj8mU+IfuFQVf1wDrGei+Jq4QrrrskCuVgKfLmEVWZx478aazUnjZcJoPrU5
+oooGKvoy/E88PDv4TDaciF2Mh5YFxg3bcfZKhhJtUmp+RQoPWIH8bU/isNXoUxP6
+f7jz3TZxZMIL+4KnUnLpfo5NMzVrUAkKGiRDSKXUWwp+oaPJOf94fdF5IpuW6qD3
+CohOHKrunIXnm6y7I91mesjLKTVOZ0/FobK0wL6ZWQKBgQD23Oj3h+7Jkn4DHsv7
+lT2MVankSL+oCdz6IY6ToSjbhhWpr6Rokrrn8EzQHXErpUK1wJDutevpe9w2oLJN
+tyealge+/CDFS+e/uFWes0pawC9ADwyS/eJ0ChPzPin8j1zdm6pJpm+icbsXew0x
+0oB4Wq12nj4f52fc/VaAhWC2SQKBgQDLPf37nXqPe75HBPHuwc767zh8YgyCPc5h
+hYMJXmiSfno+1k/UA4kUP074+dDDj4Jz1XJ5jITXeilRigsPE52p/hNinAv9i3Dz
+oi9OPzIuJrkJCuYhvzQSVvQkU8QGmWd6qqxY6sSIpBRy1s5IIoNYVJYngd4R53BX
++Z8dgB25/wKBgQCCFOpV9TUo1p68OjA2w++I0WMSvhrwCzJ8Q86DkHqdIsyre7hg
+umDu8zsmtzz5SL4cU/qLLyW/BNuHlaofNZIS7VfrlaQXEuZtqk7Dr6pQo9DCKqvv
+kQURLHZSyMELKug+hlZ3NNLCgLebqeXMCSZVRUL+rGgEG8YpNv2r/5x0sQKBgD8R
+mKwo/SxjWPOO7EiL6d/itiObBYixB0cp+DTDEC5Ngz/Wn2UKR9J5ptcKJqdY9EFf
+vitL2LlJFmNQNAhUrPtgdcGG7Q2b5Mwlywo8ACVkLM1KjYlxXQZH53EScWUe24as
+Sdk52Q0R5aqRT+gAlcOmVAYkfbYOnMUgGCk+ZXsfAoGBALsLT1qgIeegx4xdTB4j
+Ymj8eOOf0v4PDqaqRXswQinDKfZYWzrCz+HXXQuZsw02UIkZE06fH6Vp2lusUA4A
+1GzmCXfRk3HeAJwmzJ7b/YrLx9fEXvMXb787FjCSXFNcr3UJlebNMeIIKytVBokY
+y+Yt3QV8/25XqfwMZX5lVRhH
+-----END PRIVATE KEY-----

+ 11 - 0
nginx/generate-ssl.sh

@@ -0,0 +1,11 @@
+#!/bin/bash
+
+# 生成自签名 SSL 证书
+openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
+    -keyout nginx/ssl/key.pem \
+    -out nginx/ssl/cert.pem \
+    -subj "/C=CN/ST=Beijing/L=Beijing/O=SimpleKB/CN=localhost"
+
+echo "SSL 证书已生成到 nginx/ssl/ 目录"
+echo "注意: 这是自签名证书,浏览器会显示安全警告"
+echo "生产环境请使用 Let's Encrypt 或购买正式证书"

+ 32 - 0
nginx/nginx.conf

@@ -0,0 +1,32 @@
+events {
+    worker_connections 1024;
+}
+
+http {
+    include /etc/nginx/mime.types;
+    default_type application/octet-stream;
+
+    # 日志格式
+    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+                    '$status $body_bytes_sent "$http_referer" '
+                    '"$http_user_agent" "$http_x_forwarded_for"';
+
+    access_log /var/log/nginx/access.log main;
+    error_log /var/log/nginx/error.log;
+
+    # 基本配置
+    sendfile on;
+    tcp_nopush on;
+    tcp_nodelay on;
+    keepalive_timeout 65;
+    types_hash_max_size 2048;
+
+    # Gzip 压缩
+    gzip on;
+    gzip_vary on;
+    gzip_min_length 1024;
+    gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
+
+    # 包含 conf.d 目录下的配置文件
+    include /etc/nginx/conf.d/*.conf;
+}

+ 16774 - 0
package-lock.json

@@ -0,0 +1,16774 @@
+{
+  "name": "simple-kb-monorepo",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "simple-kb-monorepo",
+      "workspaces": [
+        "web",
+        "server"
+      ],
+      "devDependencies": {
+        "concurrently": "^8.2.2"
+      }
+    },
+    "node_modules/@alloc/quick-lru": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+      "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@angular-devkit/core": {
+      "version": "19.2.19",
+      "resolved": "https://registry.npmmirror.com/@angular-devkit/core/-/core-19.2.19.tgz",
+      "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "8.17.1",
+        "ajv-formats": "3.0.1",
+        "jsonc-parser": "3.3.1",
+        "picomatch": "4.0.2",
+        "rxjs": "7.8.1",
+        "source-map": "0.7.4"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "peerDependencies": {
+        "chokidar": "^4.0.0"
+      },
+      "peerDependenciesMeta": {
+        "chokidar": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/core/node_modules/picomatch": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
+      "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/@angular-devkit/core/node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@angular-devkit/core/node_modules/source-map": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@angular-devkit/schematics": {
+      "version": "19.2.19",
+      "resolved": "https://registry.npmmirror.com/@angular-devkit/schematics/-/schematics-19.2.19.tgz",
+      "integrity": "sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@angular-devkit/core": "19.2.19",
+        "jsonc-parser": "3.3.1",
+        "magic-string": "0.30.17",
+        "ora": "5.4.1",
+        "rxjs": "7.8.1"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli": {
+      "version": "19.2.19",
+      "resolved": "https://registry.npmmirror.com/@angular-devkit/schematics-cli/-/schematics-cli-19.2.19.tgz",
+      "integrity": "sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@angular-devkit/core": "19.2.19",
+        "@angular-devkit/schematics": "19.2.19",
+        "@inquirer/prompts": "7.3.2",
+        "ansi-colors": "4.1.3",
+        "symbol-observable": "4.0.0",
+        "yargs-parser": "21.1.1"
+      },
+      "bin": {
+        "schematics": "bin/schematics.js"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/checkbox": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmmirror.com/@inquirer/checkbox/-/checkbox-4.3.2.tgz",
+      "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/confirm": {
+      "version": "5.1.21",
+      "resolved": "https://registry.npmmirror.com/@inquirer/confirm/-/confirm-5.1.21.tgz",
+      "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/core": {
+      "version": "10.3.2",
+      "resolved": "https://registry.npmmirror.com/@inquirer/core/-/core-10.3.2.tgz",
+      "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "cli-width": "^4.1.0",
+        "mute-stream": "^2.0.0",
+        "signal-exit": "^4.1.0",
+        "wrap-ansi": "^6.2.0",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/editor": {
+      "version": "4.2.23",
+      "resolved": "https://registry.npmmirror.com/@inquirer/editor/-/editor-4.2.23.tgz",
+      "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/external-editor": "^1.0.3",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/expand": {
+      "version": "4.0.23",
+      "resolved": "https://registry.npmmirror.com/@inquirer/expand/-/expand-4.0.23.tgz",
+      "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/external-editor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/@inquirer/external-editor/-/external-editor-1.0.3.tgz",
+      "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chardet": "^2.1.1",
+        "iconv-lite": "^0.7.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/input": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/@inquirer/input/-/input-4.3.1.tgz",
+      "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/number": {
+      "version": "3.0.23",
+      "resolved": "https://registry.npmmirror.com/@inquirer/number/-/number-3.0.23.tgz",
+      "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/password": {
+      "version": "4.0.23",
+      "resolved": "https://registry.npmmirror.com/@inquirer/password/-/password-4.0.23.tgz",
+      "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": {
+      "version": "7.3.2",
+      "resolved": "https://registry.npmmirror.com/@inquirer/prompts/-/prompts-7.3.2.tgz",
+      "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/checkbox": "^4.1.2",
+        "@inquirer/confirm": "^5.1.6",
+        "@inquirer/editor": "^4.2.7",
+        "@inquirer/expand": "^4.0.9",
+        "@inquirer/input": "^4.1.6",
+        "@inquirer/number": "^3.0.9",
+        "@inquirer/password": "^4.0.9",
+        "@inquirer/rawlist": "^4.0.9",
+        "@inquirer/search": "^3.0.9",
+        "@inquirer/select": "^4.0.9"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/rawlist": {
+      "version": "4.1.11",
+      "resolved": "https://registry.npmmirror.com/@inquirer/rawlist/-/rawlist-4.1.11.tgz",
+      "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/search": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/@inquirer/search/-/search-3.2.2.tgz",
+      "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/select": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmmirror.com/@inquirer/select/-/select-4.4.2.tgz",
+      "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/type": {
+      "version": "3.0.10",
+      "resolved": "https://registry.npmmirror.com/@inquirer/type/-/type-3.0.10.tgz",
+      "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@angular-devkit/schematics-cli/node_modules/wrap-ansi": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@angular-devkit/schematics/node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@antfu/install-pkg": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz",
+      "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==",
+      "license": "MIT",
+      "dependencies": {
+        "package-manager-detector": "^1.3.0",
+        "tinyexec": "^1.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antfu"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz",
+      "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.27.1",
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.1.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/compat-data": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.5.tgz",
+      "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/core": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.28.5.tgz",
+      "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.27.1",
+        "@babel/generator": "^7.28.5",
+        "@babel/helper-compilation-targets": "^7.27.2",
+        "@babel/helper-module-transforms": "^7.28.3",
+        "@babel/helpers": "^7.28.4",
+        "@babel/parser": "^7.28.5",
+        "@babel/template": "^7.27.2",
+        "@babel/traverse": "^7.28.5",
+        "@babel/types": "^7.28.5",
+        "@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/core/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/generator": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.5.tgz",
+      "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/parser": "^7.28.5",
+        "@babel/types": "^7.28.5",
+        "@jridgewell/gen-mapping": "^0.3.12",
+        "@jridgewell/trace-mapping": "^0.3.28",
+        "jsesc": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.27.2",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+      "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/compat-data": "^7.27.2",
+        "@babel/helper-validator-option": "^7.27.1",
+        "browserslist": "^4.24.0",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/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/@babel/helper-compilation-targets/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/helper-globals": {
+      "version": "7.28.0",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+      "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+      "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/traverse": "^7.27.1",
+        "@babel/types": "^7.27.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.28.3",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+      "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-module-imports": "^7.27.1",
+        "@babel/helper-validator-identifier": "^7.27.1",
+        "@babel/traverse": "^7.28.3"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-plugin-utils": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+      "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.4",
+      "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.28.4.tgz",
+      "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/template": "^7.27.2",
+        "@babel/types": "^7.28.4"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.5.tgz",
+      "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/types": "^7.28.5"
+      },
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-async-generators": {
+      "version": "7.8.4",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+      "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-bigint": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
+      "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-class-properties": {
+      "version": "7.12.13",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
+      "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.12.13"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-class-static-block": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz",
+      "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.14.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-import-attributes": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz",
+      "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.27.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-import-meta": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+      "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-json-strings": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+      "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-jsx": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz",
+      "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.27.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+      "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+      "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-numeric-separator": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+      "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-object-rest-spread": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+      "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+      "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-optional-chaining": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+      "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-private-property-in-object": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz",
+      "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.14.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-top-level-await": {
+      "version": "7.14.5",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
+      "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.14.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-syntax-typescript": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz",
+      "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.27.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0-0"
+      }
+    },
+    "node_modules/@babel/plugin-transform-react-jsx-self": {
+      "version": "7.27.1",
+      "resolved": "https://registry.npmmirror.com/@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.npmmirror.com/@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/runtime": {
+      "version": "7.28.4",
+      "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.4.tgz",
+      "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/template": {
+      "version": "7.27.2",
+      "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz",
+      "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.27.1",
+        "@babel/parser": "^7.27.2",
+        "@babel/types": "^7.27.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.5.tgz",
+      "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.27.1",
+        "@babel/generator": "^7.28.5",
+        "@babel/helper-globals": "^7.28.0",
+        "@babel/parser": "^7.28.5",
+        "@babel/template": "^7.27.2",
+        "@babel/types": "^7.28.5",
+        "debug": "^4.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.28.5",
+      "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.5.tgz",
+      "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+      "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/@bcoe/v8-coverage": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmmirror.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+      "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@borewit/text-codec": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmmirror.com/@borewit/text-codec/-/text-codec-0.1.1.tgz",
+      "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
+    "node_modules/@braintree/sanitize-url": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz",
+      "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==",
+      "license": "MIT"
+    },
+    "node_modules/@cfworker/json-schema": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/@cfworker/json-schema/-/json-schema-4.1.1.tgz",
+      "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
+      "license": "MIT"
+    },
+    "node_modules/@chevrotain/cst-dts-gen": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz",
+      "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@chevrotain/gast": "11.0.3",
+        "@chevrotain/types": "11.0.3",
+        "lodash-es": "4.17.21"
+      }
+    },
+    "node_modules/@chevrotain/cst-dts-gen/node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+      "license": "MIT"
+    },
+    "node_modules/@chevrotain/gast": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz",
+      "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@chevrotain/types": "11.0.3",
+        "lodash-es": "4.17.21"
+      }
+    },
+    "node_modules/@chevrotain/gast/node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+      "license": "MIT"
+    },
+    "node_modules/@chevrotain/regexp-to-ast": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz",
+      "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@chevrotain/types": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz",
+      "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@chevrotain/utils": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz",
+      "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@colors/colors": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/@colors/colors/-/colors-1.5.0.tgz",
+      "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=0.1.90"
+      }
+    },
+    "node_modules/@cspotcode/source-map-support": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmmirror.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+      "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "0.3.9"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.9",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+      "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.0.3",
+        "@jridgewell/sourcemap-codec": "^1.4.10"
+      }
+    },
+    "node_modules/@elastic/elasticsearch": {
+      "version": "9.2.0",
+      "resolved": "https://registry.npmmirror.com/@elastic/elasticsearch/-/elasticsearch-9.2.0.tgz",
+      "integrity": "sha512-M59qmMOZOk8pTcI9Ns2ow18PlyMbYrpcXqYwkChjiyXSmmqoCTvFXkC2bGQLxrrQkXaPbYR7aZqWD9b5F1405A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@elastic/transport": "^9.2.0",
+        "apache-arrow": "18.x - 21.x",
+        "tslib": "^2.4.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@elastic/transport": {
+      "version": "9.2.3",
+      "resolved": "https://registry.npmmirror.com/@elastic/transport/-/transport-9.2.3.tgz",
+      "integrity": "sha512-BfeH5D6PXFYxZDVfy/47HhsBdV7hBPPd07LJPP5lJvstauAj3rzWkrXvce42OSuRPtI8IoR6/Pi5jNaYlptW8A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api": "1.x",
+        "@opentelemetry/core": "2.x",
+        "debug": "^4.4.1",
+        "hpagent": "^1.2.0",
+        "ms": "^2.1.3",
+        "secure-json-parse": "^4.0.0",
+        "tslib": "^2.8.1",
+        "undici": "^7.16.0"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+      "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils": {
+      "version": "4.9.0",
+      "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+      "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "eslint-visitor-keys": "^3.4.3"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+      }
+    },
+    "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmmirror.com/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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmmirror.com/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/@eslint/eslintrc/node_modules/globals": {
+      "version": "14.0.0",
+      "resolved": "https://registry.npmmirror.com/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/eslintrc/node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@eslint/js": {
+      "version": "9.39.1",
+      "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.39.1.tgz",
+      "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      }
+    },
+    "node_modules/@eslint/object-schema": {
+      "version": "2.1.7",
+      "resolved": "https://registry.npmmirror.com/@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.npmmirror.com/@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/@google/genai": {
+      "version": "1.32.0",
+      "resolved": "https://registry.npmmirror.com/@google/genai/-/genai-1.32.0.tgz",
+      "integrity": "sha512-46vaEaHAThIBlqWFTti1fo3xYU6DwCOwnIIotLhYUbNha90wk5cZL79zdf+NoAfKVsx4DPmjCtXvbQNNVPl5ZQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "google-auth-library": "^10.3.0",
+        "ws": "^8.18.0"
+      },
+      "engines": {
+        "node": ">=20.0.0"
+      },
+      "peerDependencies": {
+        "@modelcontextprotocol/sdk": "^1.24.0"
+      },
+      "peerDependenciesMeta": {
+        "@modelcontextprotocol/sdk": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@humanfs/core": {
+      "version": "0.19.1",
+      "resolved": "https://registry.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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/@iconify/types": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz",
+      "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
+      "license": "MIT"
+    },
+    "node_modules/@iconify/utils": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz",
+      "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==",
+      "license": "MIT",
+      "dependencies": {
+        "@antfu/install-pkg": "^1.1.0",
+        "@iconify/types": "^2.0.0",
+        "mlly": "^1.8.0"
+      }
+    },
+    "node_modules/@inquirer/ansi": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/@inquirer/ansi/-/ansi-1.0.2.tgz",
+      "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/figures": {
+      "version": "1.0.15",
+      "resolved": "https://registry.npmmirror.com/@inquirer/figures/-/figures-1.0.15.tgz",
+      "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/@inquirer/prompts": {
+      "version": "7.10.1",
+      "resolved": "https://registry.npmmirror.com/@inquirer/prompts/-/prompts-7.10.1.tgz",
+      "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/checkbox": "^4.3.2",
+        "@inquirer/confirm": "^5.1.21",
+        "@inquirer/editor": "^4.2.23",
+        "@inquirer/expand": "^4.0.23",
+        "@inquirer/input": "^4.3.1",
+        "@inquirer/number": "^3.0.23",
+        "@inquirer/password": "^4.0.23",
+        "@inquirer/rawlist": "^4.1.11",
+        "@inquirer/search": "^3.2.2",
+        "@inquirer/select": "^4.4.2"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/checkbox": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmmirror.com/@inquirer/checkbox/-/checkbox-4.3.2.tgz",
+      "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/confirm": {
+      "version": "5.1.21",
+      "resolved": "https://registry.npmmirror.com/@inquirer/confirm/-/confirm-5.1.21.tgz",
+      "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/core": {
+      "version": "10.3.2",
+      "resolved": "https://registry.npmmirror.com/@inquirer/core/-/core-10.3.2.tgz",
+      "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "cli-width": "^4.1.0",
+        "mute-stream": "^2.0.0",
+        "signal-exit": "^4.1.0",
+        "wrap-ansi": "^6.2.0",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/editor": {
+      "version": "4.2.23",
+      "resolved": "https://registry.npmmirror.com/@inquirer/editor/-/editor-4.2.23.tgz",
+      "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/external-editor": "^1.0.3",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/expand": {
+      "version": "4.0.23",
+      "resolved": "https://registry.npmmirror.com/@inquirer/expand/-/expand-4.0.23.tgz",
+      "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/external-editor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/@inquirer/external-editor/-/external-editor-1.0.3.tgz",
+      "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chardet": "^2.1.1",
+        "iconv-lite": "^0.7.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/input": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/@inquirer/input/-/input-4.3.1.tgz",
+      "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/number": {
+      "version": "3.0.23",
+      "resolved": "https://registry.npmmirror.com/@inquirer/number/-/number-3.0.23.tgz",
+      "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/password": {
+      "version": "4.0.23",
+      "resolved": "https://registry.npmmirror.com/@inquirer/password/-/password-4.0.23.tgz",
+      "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/rawlist": {
+      "version": "4.1.11",
+      "resolved": "https://registry.npmmirror.com/@inquirer/rawlist/-/rawlist-4.1.11.tgz",
+      "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/search": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/@inquirer/search/-/search-3.2.2.tgz",
+      "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/select": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmmirror.com/@inquirer/select/-/select-4.4.2.tgz",
+      "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@inquirer/ansi": "^1.0.2",
+        "@inquirer/core": "^10.3.2",
+        "@inquirer/figures": "^1.0.15",
+        "@inquirer/type": "^3.0.10",
+        "yoctocolors-cjs": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/@inquirer/type": {
+      "version": "3.0.10",
+      "resolved": "https://registry.npmmirror.com/@inquirer/type/-/type-3.0.10.tgz",
+      "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@types/node": ">=18"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@inquirer/prompts/node_modules/wrap-ansi": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@isaacs/balanced-match": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
+      "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/@isaacs/brace-expansion": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
+      "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@isaacs/balanced-match": "^4.0.1"
+      },
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/@isaacs/cliui": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz",
+      "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^5.1.2",
+        "string-width-cjs": "npm:string-width@^4.2.0",
+        "strip-ansi": "^7.0.1",
+        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+        "wrap-ansi": "^8.1.0",
+        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz",
+      "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz",
+      "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "license": "MIT"
+    },
+    "node_modules/@isaacs/cliui/node_modules/string-width": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz",
+      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+      "license": "MIT",
+      "dependencies": {
+        "eastasianwidth": "^0.2.0",
+        "emoji-regex": "^9.2.2",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.2.tgz",
+      "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+      "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^6.1.0",
+        "string-width": "^5.0.1",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+      "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "camelcase": "^5.3.1",
+        "find-up": "^4.1.0",
+        "get-package-type": "^0.1.0",
+        "js-yaml": "^3.13.1",
+        "resolve-from": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz",
+      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "sprintf-js": "~1.0.2"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
+      "version": "3.14.2",
+      "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.2.tgz",
+      "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "argparse": "^1.0.7",
+        "esprima": "^4.0.0"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
+    "node_modules/@istanbuljs/schema": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmmirror.com/@istanbuljs/schema/-/schema-0.1.3.tgz",
+      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@jest/console": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/console/-/console-30.2.0.tgz",
+      "integrity": "sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "chalk": "^4.1.2",
+        "jest-message-util": "30.2.0",
+        "jest-util": "30.2.0",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/core": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/core/-/core-30.2.0.tgz",
+      "integrity": "sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/console": "30.2.0",
+        "@jest/pattern": "30.0.1",
+        "@jest/reporters": "30.2.0",
+        "@jest/test-result": "30.2.0",
+        "@jest/transform": "30.2.0",
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "ansi-escapes": "^4.3.2",
+        "chalk": "^4.1.2",
+        "ci-info": "^4.2.0",
+        "exit-x": "^0.2.2",
+        "graceful-fs": "^4.2.11",
+        "jest-changed-files": "30.2.0",
+        "jest-config": "30.2.0",
+        "jest-haste-map": "30.2.0",
+        "jest-message-util": "30.2.0",
+        "jest-regex-util": "30.0.1",
+        "jest-resolve": "30.2.0",
+        "jest-resolve-dependencies": "30.2.0",
+        "jest-runner": "30.2.0",
+        "jest-runtime": "30.2.0",
+        "jest-snapshot": "30.2.0",
+        "jest-util": "30.2.0",
+        "jest-validate": "30.2.0",
+        "jest-watcher": "30.2.0",
+        "micromatch": "^4.0.8",
+        "pretty-format": "30.2.0",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@jest/diff-sequences": {
+      "version": "30.0.1",
+      "resolved": "https://registry.npmmirror.com/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz",
+      "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/environment": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/environment/-/environment-30.2.0.tgz",
+      "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/fake-timers": "30.2.0",
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "jest-mock": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/expect": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/expect/-/expect-30.2.0.tgz",
+      "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "expect": "30.2.0",
+        "jest-snapshot": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/expect-utils": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/expect-utils/-/expect-utils-30.2.0.tgz",
+      "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/get-type": "30.1.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/fake-timers": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/fake-timers/-/fake-timers-30.2.0.tgz",
+      "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.2.0",
+        "@sinonjs/fake-timers": "^13.0.0",
+        "@types/node": "*",
+        "jest-message-util": "30.2.0",
+        "jest-mock": "30.2.0",
+        "jest-util": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/get-type": {
+      "version": "30.1.0",
+      "resolved": "https://registry.npmmirror.com/@jest/get-type/-/get-type-30.1.0.tgz",
+      "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/globals": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/globals/-/globals-30.2.0.tgz",
+      "integrity": "sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/environment": "30.2.0",
+        "@jest/expect": "30.2.0",
+        "@jest/types": "30.2.0",
+        "jest-mock": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/pattern": {
+      "version": "30.0.1",
+      "resolved": "https://registry.npmmirror.com/@jest/pattern/-/pattern-30.0.1.tgz",
+      "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "jest-regex-util": "30.0.1"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/reporters": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/reporters/-/reporters-30.2.0.tgz",
+      "integrity": "sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@bcoe/v8-coverage": "^0.2.3",
+        "@jest/console": "30.2.0",
+        "@jest/test-result": "30.2.0",
+        "@jest/transform": "30.2.0",
+        "@jest/types": "30.2.0",
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "@types/node": "*",
+        "chalk": "^4.1.2",
+        "collect-v8-coverage": "^1.0.2",
+        "exit-x": "^0.2.2",
+        "glob": "^10.3.10",
+        "graceful-fs": "^4.2.11",
+        "istanbul-lib-coverage": "^3.0.0",
+        "istanbul-lib-instrument": "^6.0.0",
+        "istanbul-lib-report": "^3.0.0",
+        "istanbul-lib-source-maps": "^5.0.0",
+        "istanbul-reports": "^3.1.3",
+        "jest-message-util": "30.2.0",
+        "jest-util": "30.2.0",
+        "jest-worker": "30.2.0",
+        "slash": "^3.0.0",
+        "string-length": "^4.0.2",
+        "v8-to-istanbul": "^9.0.1"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@jest/schemas": {
+      "version": "30.0.5",
+      "resolved": "https://registry.npmmirror.com/@jest/schemas/-/schemas-30.0.5.tgz",
+      "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@sinclair/typebox": "^0.34.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/snapshot-utils": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz",
+      "integrity": "sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.2.0",
+        "chalk": "^4.1.2",
+        "graceful-fs": "^4.2.11",
+        "natural-compare": "^1.4.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/source-map": {
+      "version": "30.0.1",
+      "resolved": "https://registry.npmmirror.com/@jest/source-map/-/source-map-30.0.1.tgz",
+      "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "callsites": "^3.1.0",
+        "graceful-fs": "^4.2.11"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/test-result": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/test-result/-/test-result-30.2.0.tgz",
+      "integrity": "sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/console": "30.2.0",
+        "@jest/types": "30.2.0",
+        "@types/istanbul-lib-coverage": "^2.0.6",
+        "collect-v8-coverage": "^1.0.2"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/test-sequencer": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz",
+      "integrity": "sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/test-result": "30.2.0",
+        "graceful-fs": "^4.2.11",
+        "jest-haste-map": "30.2.0",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/transform": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/transform/-/transform-30.2.0.tgz",
+      "integrity": "sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/core": "^7.27.4",
+        "@jest/types": "30.2.0",
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "babel-plugin-istanbul": "^7.0.1",
+        "chalk": "^4.1.2",
+        "convert-source-map": "^2.0.0",
+        "fast-json-stable-stringify": "^2.1.0",
+        "graceful-fs": "^4.2.11",
+        "jest-haste-map": "30.2.0",
+        "jest-regex-util": "30.0.1",
+        "jest-util": "30.2.0",
+        "micromatch": "^4.0.8",
+        "pirates": "^4.0.7",
+        "slash": "^3.0.0",
+        "write-file-atomic": "^5.0.1"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jest/types": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/@jest/types/-/types-30.2.0.tgz",
+      "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/pattern": "30.0.1",
+        "@jest/schemas": "30.0.5",
+        "@types/istanbul-lib-coverage": "^2.0.6",
+        "@types/istanbul-reports": "^3.0.4",
+        "@types/node": "*",
+        "@types/yargs": "^17.0.33",
+        "chalk": "^4.1.2"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.13",
+      "resolved": "https://registry.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "devOptional": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.11",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.11.tgz",
+      "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.5",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+      "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.31",
+      "resolved": "https://registry.npmmirror.com/@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/@langchain/core": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.5.tgz",
+      "integrity": "sha512-m+EhnHhaCnVPJt4HRmhElBN3ZBvQGfXL/hm80UV3EHNUPNUCHi6q6de7dqrw/l4oTvmX0nC08Fm2ta1U59o1bQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@cfworker/json-schema": "^4.0.2",
+        "ansi-styles": "^5.0.0",
+        "camelcase": "6",
+        "decamelize": "1.2.0",
+        "js-tiktoken": "^1.0.12",
+        "langsmith": "^0.3.82",
+        "mustache": "^4.2.0",
+        "p-queue": "^6.6.2",
+        "uuid": "^10.0.0",
+        "zod": "^3.25.76 || ^4"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/@langchain/core/node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@langchain/langgraph": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/@langchain/langgraph/-/langgraph-1.0.4.tgz",
+      "integrity": "sha512-EYLyN/uv1ubMBd3RN/y+eAxY0FJWKrnzRw8HuDJdmDcyomgV9btyHK2zDN70sO3QDDuAU9voLNNUZeFBQkBYMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@langchain/langgraph-checkpoint": "^1.0.0",
+        "@langchain/langgraph-sdk": "~1.2.0",
+        "uuid": "^10.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@langchain/core": "^1.0.1",
+        "zod": "^3.25.32 || ^4.1.0",
+        "zod-to-json-schema": "^3.x"
+      },
+      "peerDependenciesMeta": {
+        "zod-to-json-schema": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@langchain/langgraph-checkpoint": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.0.tgz",
+      "integrity": "sha512-xrclBGvNCXDmi0Nz28t3vjpxSH6UYx6w5XAXSiiB1WEdc2xD2iY/a913I3x3a31XpInUW/GGfXXfePfaghV54A==",
+      "license": "MIT",
+      "dependencies": {
+        "uuid": "^10.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@langchain/core": "^1.0.1"
+      }
+    },
+    "node_modules/@langchain/langgraph-sdk": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/@langchain/langgraph-sdk/-/langgraph-sdk-1.2.0.tgz",
+      "integrity": "sha512-nFfNJWc9P2job2uUoL37nXfz0VW9eLEtidP0edrgeHUW7BczIQzLXC9ucJHHHGLjlK0S522kmai0abAULv3pGA==",
+      "license": "MIT",
+      "dependencies": {
+        "p-queue": "^6.6.2",
+        "p-retry": "4",
+        "uuid": "^9.0.0"
+      },
+      "peerDependencies": {
+        "@langchain/core": "^1.0.1",
+        "react": "^18 || ^19",
+        "react-dom": "^18 || ^19"
+      },
+      "peerDependenciesMeta": {
+        "@langchain/core": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        },
+        "react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@langchain/langgraph-sdk/node_modules/uuid": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz",
+      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/@langchain/openai": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmmirror.com/@langchain/openai/-/openai-1.1.3.tgz",
+      "integrity": "sha512-p+xR+4HRms5Ozjf5miC6U2AYRyNVSTdO7AMBkMYs1Tp6DWHBd+mQ72H8Ogd2dKrPuS5UDJ5dbpI1fS+OrTbgQQ==",
+      "license": "MIT",
+      "dependencies": {
+        "js-tiktoken": "^1.0.12",
+        "openai": "^6.9.0",
+        "zod": "^3.25.76 || ^4"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "peerDependencies": {
+        "@langchain/core": "^1.0.0"
+      }
+    },
+    "node_modules/@langchain/textsplitters": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/@langchain/textsplitters/-/textsplitters-1.0.1.tgz",
+      "integrity": "sha512-rheJlB01iVtrOUzttscutRgLybPH9qR79EyzBEbf1u97ljWyuxQfCwIWK+SjoQTM9O8M7GGLLRBSYE26Jmcoww==",
+      "license": "MIT",
+      "dependencies": {
+        "js-tiktoken": "^1.0.12"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "peerDependencies": {
+        "@langchain/core": "^1.0.0"
+      }
+    },
+    "node_modules/@lukeed/csprng": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/@lukeed/csprng/-/csprng-1.1.0.tgz",
+      "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@mermaid-js/parser": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.3.tgz",
+      "integrity": "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==",
+      "license": "MIT",
+      "dependencies": {
+        "langium": "3.3.1"
+      }
+    },
+    "node_modules/@napi-rs/canvas": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.88.tgz",
+      "integrity": "sha512-/p08f93LEbsL5mDZFQ3DBxcPv/I4QG9EDYRRq1WNlCOXVfAHBTHMSVMwxlqG/AtnSfUr9+vgfN7MKiyDo0+Weg==",
+      "license": "MIT",
+      "optional": true,
+      "workspaces": [
+        "e2e/*"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      },
+      "optionalDependencies": {
+        "@napi-rs/canvas-android-arm64": "0.1.88",
+        "@napi-rs/canvas-darwin-arm64": "0.1.88",
+        "@napi-rs/canvas-darwin-x64": "0.1.88",
+        "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.88",
+        "@napi-rs/canvas-linux-arm64-gnu": "0.1.88",
+        "@napi-rs/canvas-linux-arm64-musl": "0.1.88",
+        "@napi-rs/canvas-linux-riscv64-gnu": "0.1.88",
+        "@napi-rs/canvas-linux-x64-gnu": "0.1.88",
+        "@napi-rs/canvas-linux-x64-musl": "0.1.88",
+        "@napi-rs/canvas-win32-arm64-msvc": "0.1.88",
+        "@napi-rs/canvas-win32-x64-msvc": "0.1.88"
+      }
+    },
+    "node_modules/@napi-rs/canvas-android-arm64": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.88.tgz",
+      "integrity": "sha512-KEaClPnZuVxJ8smUWjV1wWFkByBO/D+vy4lN+Dm5DFH514oqwukxKGeck9xcKJhaWJGjfruGmYGiwRe//+/zQQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@napi-rs/canvas-darwin-arm64": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.88.tgz",
+      "integrity": "sha512-Xgywz0dDxOKSgx3eZnK85WgGMmGrQEW7ZLA/E7raZdlEE+xXCozobgqz2ZvYigpB6DJFYkqnwHjqCOTSDGlFdg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@napi-rs/canvas-darwin-x64": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.88.tgz",
+      "integrity": "sha512-Yz4wSCIQOUgNucgk+8NFtQxQxZV5NO8VKRl9ePKE6XoNyNVC8JDqtvhh3b3TPqKK8W5p2EQpAr1rjjm0mfBxdg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.88.tgz",
+      "integrity": "sha512-9gQM2SlTo76hYhxHi2XxWTAqpTOb+JtxMPEIr+H5nAhHhyEtNmTSDRtz93SP7mGd2G3Ojf2oF5tP9OdgtgXyKg==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-arm64-gnu": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.88.tgz",
+      "integrity": "sha512-7qgaOBMXuVRk9Fzztzr3BchQKXDxGbY+nwsovD3I/Sx81e+sX0ReEDYHTItNb0Je4NHbAl7D0MKyd4SvUc04sg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-arm64-musl": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.88.tgz",
+      "integrity": "sha512-kYyNrUsHLkoGHBc77u4Unh067GrfiCUMbGHC2+OTxbeWfZkPt2o32UOQkhnSswKd9Fko/wSqqGkY956bIUzruA==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.88.tgz",
+      "integrity": "sha512-HVuH7QgzB0yavYdNZDRyAsn/ejoXB0hn8twwFnOqUbCCdkV+REna7RXjSR7+PdfW0qMQ2YYWsLvVBT5iL/mGpw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-x64-gnu": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.88.tgz",
+      "integrity": "sha512-hvcvKIcPEQrvvJtJnwD35B3qk6umFJ8dFIr8bSymfrSMem0EQsfn1ztys8ETIFndTwdNWJKWluvxztA41ivsEw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-x64-musl": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.88.tgz",
+      "integrity": "sha512-eSMpGYY2xnZSQ6UxYJ6plDboxq4KeJ4zT5HaVkUnbObNN6DlbJe0Mclh3wifAmquXfrlgTZt6zhHsUgz++AK6g==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@napi-rs/canvas-win32-arm64-msvc": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.88.tgz",
+      "integrity": "sha512-qcIFfEgHrchyYqRrxsCeTQgpJZ/GqHiqPcU/Fvw/ARVlQeDX1VyFH+X+0gCR2tca6UJrq96vnW+5o7buCq+erA==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@napi-rs/canvas-win32-x64-msvc": {
+      "version": "0.1.88",
+      "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.88.tgz",
+      "integrity": "sha512-ROVqbfS4QyZxYkqmaIBBpbz/BQvAR+05FXM5PAtTYVc0uyY8Y4BHJSMdGAaMf6TdIVRsQsiq+FG/dH9XhvWCFQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Brooooooklyn"
+      }
+    },
+    "node_modules/@nestjs/cli": {
+      "version": "11.0.14",
+      "resolved": "https://registry.npmmirror.com/@nestjs/cli/-/cli-11.0.14.tgz",
+      "integrity": "sha512-YwP03zb5VETTwelXU+AIzMVbEZKk/uxJL+z9pw0mdG9ogAtqZ6/mpmIM4nEq/NU8D0a7CBRLcMYUmWW/55pfqw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@angular-devkit/core": "19.2.19",
+        "@angular-devkit/schematics": "19.2.19",
+        "@angular-devkit/schematics-cli": "19.2.19",
+        "@inquirer/prompts": "7.10.1",
+        "@nestjs/schematics": "^11.0.1",
+        "ansis": "4.2.0",
+        "chokidar": "4.0.3",
+        "cli-table3": "0.6.5",
+        "commander": "4.1.1",
+        "fork-ts-checker-webpack-plugin": "9.1.0",
+        "glob": "13.0.0",
+        "node-emoji": "1.11.0",
+        "ora": "5.4.1",
+        "tsconfig-paths": "4.2.0",
+        "tsconfig-paths-webpack-plugin": "4.2.0",
+        "typescript": "5.9.3",
+        "webpack": "5.103.0",
+        "webpack-node-externals": "3.0.0"
+      },
+      "bin": {
+        "nest": "bin/nest.js"
+      },
+      "engines": {
+        "node": ">= 20.11"
+      },
+      "peerDependencies": {
+        "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0",
+        "@swc/core": "^1.3.62"
+      },
+      "peerDependenciesMeta": {
+        "@swc/cli": {
+          "optional": true
+        },
+        "@swc/core": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/cli/node_modules/glob": {
+      "version": "13.0.0",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-13.0.0.tgz",
+      "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "minimatch": "^10.1.1",
+        "minipass": "^7.1.2",
+        "path-scurry": "^2.0.0"
+      },
+      "engines": {
+        "node": "20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@nestjs/cli/node_modules/lru-cache": {
+      "version": "11.2.4",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.2.4.tgz",
+      "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "engines": {
+        "node": "20 || >=22"
+      }
+    },
+    "node_modules/@nestjs/cli/node_modules/minimatch": {
+      "version": "10.1.1",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.1.1.tgz",
+      "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "@isaacs/brace-expansion": "^5.0.0"
+      },
+      "engines": {
+        "node": "20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@nestjs/cli/node_modules/path-scurry": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-2.0.1.tgz",
+      "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==",
+      "dev": true,
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "lru-cache": "^11.0.0",
+        "minipass": "^7.1.2"
+      },
+      "engines": {
+        "node": "20 || >=22"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@nestjs/common": {
+      "version": "11.1.9",
+      "resolved": "https://registry.npmmirror.com/@nestjs/common/-/common-11.1.9.tgz",
+      "integrity": "sha512-zDntUTReRbAThIfSp3dQZ9kKqI+LjgLp5YZN5c1bgNRDuoeLySAoZg46Bg1a+uV8TMgIRziHocglKGNzr6l+bQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "file-type": "21.1.0",
+        "iterare": "1.2.1",
+        "load-esm": "1.0.3",
+        "tslib": "2.8.1",
+        "uid": "2.0.2"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nest"
+      },
+      "peerDependencies": {
+        "class-transformer": ">=0.4.1",
+        "class-validator": ">=0.13.2",
+        "reflect-metadata": "^0.1.12 || ^0.2.0",
+        "rxjs": "^7.1.0"
+      },
+      "peerDependenciesMeta": {
+        "class-transformer": {
+          "optional": true
+        },
+        "class-validator": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/config": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/@nestjs/config/-/config-4.0.2.tgz",
+      "integrity": "sha512-McMW6EXtpc8+CwTUwFdg6h7dYcBUpH5iUILCclAsa+MbCEvC9ZKu4dCHRlJqALuhjLw97pbQu62l4+wRwGeZqA==",
+      "license": "MIT",
+      "dependencies": {
+        "dotenv": "16.4.7",
+        "dotenv-expand": "12.0.1",
+        "lodash": "4.17.21"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^10.0.0 || ^11.0.0",
+        "rxjs": "^7.1.0"
+      }
+    },
+    "node_modules/@nestjs/config/node_modules/dotenv": {
+      "version": "16.4.7",
+      "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.4.7.tgz",
+      "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/@nestjs/core": {
+      "version": "11.1.9",
+      "resolved": "https://registry.npmmirror.com/@nestjs/core/-/core-11.1.9.tgz",
+      "integrity": "sha512-a00B0BM4X+9z+t3UxJqIZlemIwCQdYoPKrMcM+ky4z3pkqqG1eTWexjs+YXpGObnLnjtMPVKWlcZHp3adDYvUw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@nuxt/opencollective": "0.4.1",
+        "fast-safe-stringify": "2.1.1",
+        "iterare": "1.2.1",
+        "path-to-regexp": "8.3.0",
+        "tslib": "2.8.1",
+        "uid": "2.0.2"
+      },
+      "engines": {
+        "node": ">= 20"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nest"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^11.0.0",
+        "@nestjs/microservices": "^11.0.0",
+        "@nestjs/platform-express": "^11.0.0",
+        "@nestjs/websockets": "^11.0.0",
+        "reflect-metadata": "^0.1.12 || ^0.2.0",
+        "rxjs": "^7.1.0"
+      },
+      "peerDependenciesMeta": {
+        "@nestjs/microservices": {
+          "optional": true
+        },
+        "@nestjs/platform-express": {
+          "optional": true
+        },
+        "@nestjs/websockets": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/jwt": {
+      "version": "11.0.2",
+      "resolved": "https://registry.npmmirror.com/@nestjs/jwt/-/jwt-11.0.2.tgz",
+      "integrity": "sha512-rK8aE/3/Ma45gAWfCksAXUNbOoSOUudU0Kn3rT39htPF7wsYXtKfjALKeKKJbFrIWbLjsbqfXX5bIJNvgBugGA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/jsonwebtoken": "9.0.10",
+        "jsonwebtoken": "9.0.3"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0"
+      }
+    },
+    "node_modules/@nestjs/mapped-types": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz",
+      "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@nestjs/common": "^10.0.0 || ^11.0.0",
+        "class-transformer": "^0.4.0 || ^0.5.0",
+        "class-validator": "^0.13.0 || ^0.14.0",
+        "reflect-metadata": "^0.1.12 || ^0.2.0"
+      },
+      "peerDependenciesMeta": {
+        "class-transformer": {
+          "optional": true
+        },
+        "class-validator": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/passport": {
+      "version": "11.0.5",
+      "resolved": "https://registry.npmmirror.com/@nestjs/passport/-/passport-11.0.5.tgz",
+      "integrity": "sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@nestjs/common": "^10.0.0 || ^11.0.0",
+        "passport": "^0.5.0 || ^0.6.0 || ^0.7.0"
+      }
+    },
+    "node_modules/@nestjs/platform-express": {
+      "version": "11.1.9",
+      "resolved": "https://registry.npmmirror.com/@nestjs/platform-express/-/platform-express-11.1.9.tgz",
+      "integrity": "sha512-GVd3+0lO0mJq2m1kl9hDDnVrX3Nd4oH3oDfklz0pZEVEVS0KVSp63ufHq2Lu9cyPdSBuelJr9iPm2QQ1yX+Kmw==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "cors": "2.8.5",
+        "express": "5.1.0",
+        "multer": "2.0.2",
+        "path-to-regexp": "8.3.0",
+        "tslib": "2.8.1"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nest"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^11.0.0",
+        "@nestjs/core": "^11.0.0"
+      }
+    },
+    "node_modules/@nestjs/schedule": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.1.0.tgz",
+      "integrity": "sha512-W25Ydc933Gzb1/oo7+bWzzDiOissE+h/dhIAPugA39b9MuIzBbLybuXpc1AjoQLczO3v0ldmxaffVl87W0uqoQ==",
+      "license": "MIT",
+      "dependencies": {
+        "cron": "4.3.5"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^10.0.0 || ^11.0.0",
+        "@nestjs/core": "^10.0.0 || ^11.0.0"
+      }
+    },
+    "node_modules/@nestjs/schematics": {
+      "version": "11.0.9",
+      "resolved": "https://registry.npmmirror.com/@nestjs/schematics/-/schematics-11.0.9.tgz",
+      "integrity": "sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@angular-devkit/core": "19.2.17",
+        "@angular-devkit/schematics": "19.2.17",
+        "comment-json": "4.4.1",
+        "jsonc-parser": "3.3.1",
+        "pluralize": "8.0.0"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.2"
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": {
+      "version": "19.2.17",
+      "resolved": "https://registry.npmmirror.com/@angular-devkit/core/-/core-19.2.17.tgz",
+      "integrity": "sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "8.17.1",
+        "ajv-formats": "3.0.1",
+        "jsonc-parser": "3.3.1",
+        "picomatch": "4.0.2",
+        "rxjs": "7.8.1",
+        "source-map": "0.7.4"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      },
+      "peerDependencies": {
+        "chokidar": "^4.0.0"
+      },
+      "peerDependenciesMeta": {
+        "chokidar": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": {
+      "version": "19.2.17",
+      "resolved": "https://registry.npmmirror.com/@angular-devkit/schematics/-/schematics-19.2.17.tgz",
+      "integrity": "sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@angular-devkit/core": "19.2.17",
+        "jsonc-parser": "3.3.1",
+        "magic-string": "0.30.17",
+        "ora": "5.4.1",
+        "rxjs": "7.8.1"
+      },
+      "engines": {
+        "node": "^18.19.1 || ^20.11.1 || >=22.0.0",
+        "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+        "yarn": ">= 1.13.0"
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/picomatch": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.2.tgz",
+      "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@nestjs/schematics/node_modules/source-map": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nestjs/serve-static": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/@nestjs/serve-static/-/serve-static-5.0.4.tgz",
+      "integrity": "sha512-3kO1M9D3vsPyWPFardxIjUYeuolS58PnhCoBTkS7t3BrdZFZCKHnBZ15js+UOzOR2Q6HmD7ssGjLd0DVYVdvOw==",
+      "license": "MIT",
+      "dependencies": {
+        "path-to-regexp": "8.3.0"
+      },
+      "peerDependencies": {
+        "@fastify/static": "^8.0.4",
+        "@nestjs/common": "^11.0.2",
+        "@nestjs/core": "^11.0.2",
+        "express": "^5.0.1",
+        "fastify": "^5.2.1"
+      },
+      "peerDependenciesMeta": {
+        "@fastify/static": {
+          "optional": true
+        },
+        "express": {
+          "optional": true
+        },
+        "fastify": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/testing": {
+      "version": "11.1.9",
+      "resolved": "https://registry.npmmirror.com/@nestjs/testing/-/testing-11.1.9.tgz",
+      "integrity": "sha512-UFxerBDdb0RUNxQNj25pvkvNE7/vxKhXYWBt3QuwBFnYISzRIzhVlyIqLfoV5YI3zV0m0Nn4QAn1KM0zzwfEng==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "2.8.1"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/nest"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^11.0.0",
+        "@nestjs/core": "^11.0.0",
+        "@nestjs/microservices": "^11.0.0",
+        "@nestjs/platform-express": "^11.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@nestjs/microservices": {
+          "optional": true
+        },
+        "@nestjs/platform-express": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@nestjs/typeorm": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmmirror.com/@nestjs/typeorm/-/typeorm-11.0.0.tgz",
+      "integrity": "sha512-SOeUQl70Lb2OfhGkvnh4KXWlsd+zA08RuuQgT7kKbzivngxzSo1Oc7Usu5VxCxACQC9wc2l9esOHILSJeK7rJA==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@nestjs/common": "^10.0.0 || ^11.0.0",
+        "@nestjs/core": "^10.0.0 || ^11.0.0",
+        "reflect-metadata": "^0.1.13 || ^0.2.0",
+        "rxjs": "^7.2.0",
+        "typeorm": "^0.3.0"
+      }
+    },
+    "node_modules/@noble/hashes": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmmirror.com/@noble/hashes/-/hashes-1.8.0.tgz",
+      "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^14.21.3 || >=16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/@nuxt/opencollective": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/@nuxt/opencollective/-/opencollective-0.4.1.tgz",
+      "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==",
+      "license": "MIT",
+      "dependencies": {
+        "consola": "^3.2.3"
+      },
+      "bin": {
+        "opencollective": "bin/opencollective.js"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.10.0",
+        "npm": ">=5.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/api": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmmirror.com/@opentelemetry/api/-/api-1.9.0.tgz",
+      "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+      "license": "Apache-2.0",
+      "peer": true,
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@opentelemetry/core": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/@opentelemetry/core/-/core-2.2.0.tgz",
+      "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/semantic-conventions": "^1.29.0"
+      },
+      "engines": {
+        "node": "^18.19.0 || >=20.6.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/semantic-conventions": {
+      "version": "1.38.0",
+      "resolved": "https://registry.npmmirror.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.38.0.tgz",
+      "integrity": "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@paralleldrive/cuid2": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz",
+      "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "^1.1.5"
+      }
+    },
+    "node_modules/@pdf-lib/standard-fonts": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
+      "integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
+      "license": "MIT",
+      "dependencies": {
+        "pako": "^1.0.6"
+      }
+    },
+    "node_modules/@pdf-lib/upng": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
+      "integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
+      "license": "MIT",
+      "dependencies": {
+        "pako": "^1.0.10"
+      }
+    },
+    "node_modules/@pkgjs/parseargs": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@pkgr/core": {
+      "version": "0.2.9",
+      "resolved": "https://registry.npmmirror.com/@pkgr/core/-/core-0.2.9.tgz",
+      "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/pkgr"
+      }
+    },
+    "node_modules/@rolldown/pluginutils": {
+      "version": "1.0.0-beta.53",
+      "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz",
+      "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@rollup/rollup-win32-x64-gnu": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
+      "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
+      "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@sinclair/typebox": {
+      "version": "0.34.41",
+      "resolved": "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.34.41.tgz",
+      "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@sinonjs/commons": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/@sinonjs/commons/-/commons-3.0.1.tgz",
+      "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "node_modules/@sinonjs/fake-timers": {
+      "version": "13.0.5",
+      "resolved": "https://registry.npmmirror.com/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz",
+      "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@sinonjs/commons": "^3.0.1"
+      }
+    },
+    "node_modules/@sqltools/formatter": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmmirror.com/@sqltools/formatter/-/formatter-1.2.5.tgz",
+      "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==",
+      "license": "MIT"
+    },
+    "node_modules/@swc/helpers": {
+      "version": "0.5.17",
+      "resolved": "https://registry.npmmirror.com/@swc/helpers/-/helpers-0.5.17.tgz",
+      "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.8.0"
+      }
+    },
+    "node_modules/@tailwindcss/typography": {
+      "version": "0.5.19",
+      "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz",
+      "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "postcss-selector-parser": "6.0.10"
+      },
+      "peerDependencies": {
+        "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
+      }
+    },
+    "node_modules/@tokenizer/inflate": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmmirror.com/@tokenizer/inflate/-/inflate-0.3.1.tgz",
+      "integrity": "sha512-4oeoZEBQdLdt5WmP/hx1KZ6D3/Oid/0cUb2nk4F0pTDAWy+KCH3/EnAkZF/bvckWo8I33EqBm01lIPgmgc8rCA==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.4.1",
+        "fflate": "^0.8.2",
+        "token-types": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
+    "node_modules/@tokenizer/token": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/@tokenizer/token/-/token-0.3.0.tgz",
+      "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
+      "license": "MIT"
+    },
+    "node_modules/@tsconfig/node10": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.12.tgz",
+      "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/@tsconfig/node12": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/@tsconfig/node12/-/node12-1.0.11.tgz",
+      "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/@tsconfig/node14": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/@tsconfig/node14/-/node14-1.0.3.tgz",
+      "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/@tsconfig/node16": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz",
+      "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/babel__core": {
+      "version": "7.20.5",
+      "resolved": "https://registry.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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/bcrypt": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/@types/bcrypt/-/bcrypt-6.0.0.tgz",
+      "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/better-sqlite3": {
+      "version": "7.6.13",
+      "resolved": "https://registry.npmmirror.com/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
+      "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/body-parser": {
+      "version": "1.19.6",
+      "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.6.tgz",
+      "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/connect": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/command-line-args": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmmirror.com/@types/command-line-args/-/command-line-args-5.2.3.tgz",
+      "integrity": "sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/command-line-usage": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmmirror.com/@types/command-line-usage/-/command-line-usage-5.0.4.tgz",
+      "integrity": "sha512-BwR5KP3Es/CSht0xqBcUXS3qCAUVXwpRKsV2+arxeb65atasuXG9LykC9Ab10Cw3s2raH92ZqOeILaQbsB2ACg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.38",
+      "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz",
+      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/cookiejar": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmmirror.com/@types/cookiejar/-/cookiejar-2.1.5.tgz",
+      "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/cron": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/@types/cron/-/cron-2.0.1.tgz",
+      "integrity": "sha512-WHa/1rtNtD2Q/H0+YTTZoty+/5rcE66iAFX2IY+JuUoOACsevYyFkSYu/2vdw+G5LrmO7Lxowrqm0av4k3qWNQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/luxon": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/d3": {
+      "version": "7.4.3",
+      "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz",
+      "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-array": "*",
+        "@types/d3-axis": "*",
+        "@types/d3-brush": "*",
+        "@types/d3-chord": "*",
+        "@types/d3-color": "*",
+        "@types/d3-contour": "*",
+        "@types/d3-delaunay": "*",
+        "@types/d3-dispatch": "*",
+        "@types/d3-drag": "*",
+        "@types/d3-dsv": "*",
+        "@types/d3-ease": "*",
+        "@types/d3-fetch": "*",
+        "@types/d3-force": "*",
+        "@types/d3-format": "*",
+        "@types/d3-geo": "*",
+        "@types/d3-hierarchy": "*",
+        "@types/d3-interpolate": "*",
+        "@types/d3-path": "*",
+        "@types/d3-polygon": "*",
+        "@types/d3-quadtree": "*",
+        "@types/d3-random": "*",
+        "@types/d3-scale": "*",
+        "@types/d3-scale-chromatic": "*",
+        "@types/d3-selection": "*",
+        "@types/d3-shape": "*",
+        "@types/d3-time": "*",
+        "@types/d3-time-format": "*",
+        "@types/d3-timer": "*",
+        "@types/d3-transition": "*",
+        "@types/d3-zoom": "*"
+      }
+    },
+    "node_modules/@types/d3-array": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
+      "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-axis": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz",
+      "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-selection": "*"
+      }
+    },
+    "node_modules/@types/d3-brush": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz",
+      "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-selection": "*"
+      }
+    },
+    "node_modules/@types/d3-chord": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz",
+      "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-color": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+      "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-contour": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz",
+      "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-array": "*",
+        "@types/geojson": "*"
+      }
+    },
+    "node_modules/@types/d3-delaunay": {
+      "version": "6.0.4",
+      "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+      "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-dispatch": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz",
+      "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-drag": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz",
+      "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-selection": "*"
+      }
+    },
+    "node_modules/@types/d3-dsv": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz",
+      "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-ease": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+      "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-fetch": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz",
+      "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-dsv": "*"
+      }
+    },
+    "node_modules/@types/d3-force": {
+      "version": "3.0.10",
+      "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz",
+      "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-format": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
+      "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-geo": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz",
+      "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/geojson": "*"
+      }
+    },
+    "node_modules/@types/d3-hierarchy": {
+      "version": "3.1.7",
+      "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz",
+      "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-interpolate": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+      "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-color": "*"
+      }
+    },
+    "node_modules/@types/d3-path": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+      "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-polygon": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz",
+      "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-quadtree": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz",
+      "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-random": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz",
+      "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-scale": {
+      "version": "4.0.9",
+      "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+      "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-time": "*"
+      }
+    },
+    "node_modules/@types/d3-scale-chromatic": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+      "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-selection": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz",
+      "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-shape": {
+      "version": "3.1.8",
+      "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz",
+      "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-path": "*"
+      }
+    },
+    "node_modules/@types/d3-time": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+      "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-time-format": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
+      "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-timer": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+      "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/d3-transition": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz",
+      "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-selection": "*"
+      }
+    },
+    "node_modules/@types/d3-zoom": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz",
+      "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/d3-interpolate": "*",
+        "@types/d3-selection": "*"
+      }
+    },
+    "node_modules/@types/debug": {
+      "version": "4.1.12",
+      "resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.12.tgz",
+      "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/ms": "*"
+      }
+    },
+    "node_modules/@types/eslint": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmmirror.com/@types/eslint/-/eslint-9.6.1.tgz",
+      "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/estree": "*",
+        "@types/json-schema": "*"
+      }
+    },
+    "node_modules/@types/eslint-scope": {
+      "version": "3.7.7",
+      "resolved": "https://registry.npmmirror.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
+      "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/eslint": "*",
+        "@types/estree": "*"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz",
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "license": "MIT"
+    },
+    "node_modules/@types/estree-jsx": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+      "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "*"
+      }
+    },
+    "node_modules/@types/express": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmmirror.com/@types/express/-/express-5.0.6.tgz",
+      "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/body-parser": "*",
+        "@types/express-serve-static-core": "^5.0.0",
+        "@types/serve-static": "^2"
+      }
+    },
+    "node_modules/@types/express-serve-static-core": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-5.1.0.tgz",
+      "integrity": "sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "@types/qs": "*",
+        "@types/range-parser": "*",
+        "@types/send": "*"
+      }
+    },
+    "node_modules/@types/geojson": {
+      "version": "7946.0.16",
+      "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
+      "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/hast": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@types/hast/-/hast-3.0.4.tgz",
+      "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "*"
+      }
+    },
+    "node_modules/@types/http-errors": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.5.tgz",
+      "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/istanbul-lib-coverage": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmmirror.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
+      "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/istanbul-lib-report": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz",
+      "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/istanbul-lib-coverage": "*"
+      }
+    },
+    "node_modules/@types/istanbul-reports": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmmirror.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz",
+      "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/istanbul-lib-report": "*"
+      }
+    },
+    "node_modules/@types/jest": {
+      "version": "30.0.0",
+      "resolved": "https://registry.npmmirror.com/@types/jest/-/jest-30.0.0.tgz",
+      "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "expect": "^30.0.0",
+        "pretty-format": "^30.0.0"
+      }
+    },
+    "node_modules/@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/jsonwebtoken": {
+      "version": "9.0.10",
+      "resolved": "https://registry.npmmirror.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz",
+      "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/ms": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/katex": {
+      "version": "0.16.8",
+      "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz",
+      "integrity": "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/luxon": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz",
+      "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==",
+      "license": "MIT"
+    },
+    "node_modules/@types/mdast": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-4.0.4.tgz",
+      "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "*"
+      }
+    },
+    "node_modules/@types/methods": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/@types/methods/-/methods-1.1.4.tgz",
+      "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/ms": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz",
+      "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/multer": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.0.0.tgz",
+      "integrity": "sha512-C3Z9v9Evij2yST3RSBktxP9STm6OdMc5uR1xF1SGr98uv8dUlAL2hqwrZ3GVB3uyMyiegnscEK6PGtYvNrjTjw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/express": "*"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "25.0.0",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-25.0.0.tgz",
+      "integrity": "sha512-rl78HwuZlaDIUSeUKkmogkhebA+8K1Hy7tddZuJ3D0xV8pZSfsYGTsliGUol1JPzu9EKnTxPC4L1fiWouStRew==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "undici-types": "~7.16.0"
+      }
+    },
+    "node_modules/@types/passport": {
+      "version": "1.0.17",
+      "resolved": "https://registry.npmmirror.com/@types/passport/-/passport-1.0.17.tgz",
+      "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/express": "*"
+      }
+    },
+    "node_modules/@types/passport-jwt": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/@types/passport-jwt/-/passport-jwt-4.0.1.tgz",
+      "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/jsonwebtoken": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "node_modules/@types/passport-local": {
+      "version": "1.0.38",
+      "resolved": "https://registry.npmmirror.com/@types/passport-local/-/passport-local-1.0.38.tgz",
+      "integrity": "sha512-nsrW4A963lYE7lNTv9cr5WmiUD1ibYJvWrpE13oxApFsRt77b0RdtZvKbCdNIY4v/QZ6TRQWaDDEwV1kCTmcXg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/express": "*",
+        "@types/passport": "*",
+        "@types/passport-strategy": "*"
+      }
+    },
+    "node_modules/@types/passport-strategy": {
+      "version": "0.2.38",
+      "resolved": "https://registry.npmmirror.com/@types/passport-strategy/-/passport-strategy-0.2.38.tgz",
+      "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/express": "*",
+        "@types/passport": "*"
+      }
+    },
+    "node_modules/@types/prismjs": {
+      "version": "1.26.5",
+      "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz",
+      "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/qs": {
+      "version": "6.14.0",
+      "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.14.0.tgz",
+      "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/range-parser": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz",
+      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/react": {
+      "version": "19.2.8",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.8.tgz",
+      "integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "csstype": "^3.2.2"
+      }
+    },
+    "node_modules/@types/react-syntax-highlighter": {
+      "version": "15.5.13",
+      "resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz",
+      "integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/react": "*"
+      }
+    },
+    "node_modules/@types/retry": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmmirror.com/@types/retry/-/retry-0.12.0.tgz",
+      "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/send": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/@types/send/-/send-1.2.1.tgz",
+      "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/serve-static": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-2.2.0.tgz",
+      "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-errors": "*",
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/stack-utils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/@types/stack-utils/-/stack-utils-2.0.3.tgz",
+      "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/superagent": {
+      "version": "8.1.9",
+      "resolved": "https://registry.npmmirror.com/@types/superagent/-/superagent-8.1.9.tgz",
+      "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/cookiejar": "^2.1.5",
+        "@types/methods": "^1.1.4",
+        "@types/node": "*",
+        "form-data": "^4.0.0"
+      }
+    },
+    "node_modules/@types/supertest": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/@types/supertest/-/supertest-6.0.3.tgz",
+      "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/methods": "^1.1.4",
+        "@types/superagent": "^8.1.0"
+      }
+    },
+    "node_modules/@types/trusted-types": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+      "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/@types/unist": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz",
+      "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+      "license": "MIT"
+    },
+    "node_modules/@types/uuid": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmmirror.com/@types/uuid/-/uuid-10.0.0.tgz",
+      "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
+      "license": "MIT"
+    },
+    "node_modules/@types/validator": {
+      "version": "13.15.10",
+      "resolved": "https://registry.npmmirror.com/@types/validator/-/validator-13.15.10.tgz",
+      "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/yargs": {
+      "version": "17.0.35",
+      "resolved": "https://registry.npmmirror.com/@types/yargs/-/yargs-17.0.35.tgz",
+      "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/yargs-parser": "*"
+      }
+    },
+    "node_modules/@types/yargs-parser": {
+      "version": "21.0.3",
+      "resolved": "https://registry.npmmirror.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz",
+      "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@typescript-eslint/eslint-plugin": {
+      "version": "8.49.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz",
+      "integrity": "sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/regexpp": "^4.10.0",
+        "@typescript-eslint/scope-manager": "8.49.0",
+        "@typescript-eslint/type-utils": "8.49.0",
+        "@typescript-eslint/utils": "8.49.0",
+        "@typescript-eslint/visitor-keys": "8.49.0",
+        "ignore": "^7.0.0",
+        "natural-compare": "^1.4.0",
+        "ts-api-utils": "^2.1.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "@typescript-eslint/parser": "^8.49.0",
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+      "version": "7.0.5",
+      "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz",
+      "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/@typescript-eslint/parser": {
+      "version": "8.49.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.49.0.tgz",
+      "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@typescript-eslint/scope-manager": "8.49.0",
+        "@typescript-eslint/types": "8.49.0",
+        "@typescript-eslint/typescript-estree": "8.49.0",
+        "@typescript-eslint/visitor-keys": "8.49.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/project-service": {
+      "version": "8.49.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.49.0.tgz",
+      "integrity": "sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/tsconfig-utils": "^8.49.0",
+        "@typescript-eslint/types": "^8.49.0",
+        "debug": "^4.3.4"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/scope-manager": {
+      "version": "8.49.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz",
+      "integrity": "sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.49.0",
+        "@typescript-eslint/visitor-keys": "8.49.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.49.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz",
+      "integrity": "sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA==",
+      "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.49.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz",
+      "integrity": "sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.49.0",
+        "@typescript-eslint/typescript-estree": "8.49.0",
+        "@typescript-eslint/utils": "8.49.0",
+        "debug": "^4.3.4",
+        "ts-api-utils": "^2.1.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/types": {
+      "version": "8.49.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.49.0.tgz",
+      "integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==",
+      "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.49.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz",
+      "integrity": "sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/project-service": "8.49.0",
+        "@typescript-eslint/tsconfig-utils": "8.49.0",
+        "@typescript-eslint/types": "8.49.0",
+        "@typescript-eslint/visitor-keys": "8.49.0",
+        "debug": "^4.3.4",
+        "minimatch": "^9.0.4",
+        "semver": "^7.6.0",
+        "tinyglobby": "^0.2.15",
+        "ts-api-utils": "^2.1.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/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.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/@typescript-eslint/utils": {
+      "version": "8.49.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.49.0.tgz",
+      "integrity": "sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.7.0",
+        "@typescript-eslint/scope-manager": "8.49.0",
+        "@typescript-eslint/types": "8.49.0",
+        "@typescript-eslint/typescript-estree": "8.49.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/@typescript-eslint/visitor-keys": {
+      "version": "8.49.0",
+      "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz",
+      "integrity": "sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/types": "8.49.0",
+        "eslint-visitor-keys": "^4.2.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      }
+    },
+    "node_modules/@ungap/structured-clone": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+      "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+      "license": "ISC"
+    },
+    "node_modules/@unrs/resolver-binding-win32-x64-msvc": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmmirror.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz",
+      "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@vitejs/plugin-react": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz",
+      "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/core": "^7.28.5",
+        "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+        "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+        "@rolldown/pluginutils": "1.0.0-beta.53",
+        "@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/@webassemblyjs/ast": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.14.1.tgz",
+      "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/helper-numbers": "1.13.2",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
+      }
+    },
+    "node_modules/@webassemblyjs/floating-point-hex-parser": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
+      "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-api-error": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
+      "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-buffer": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
+      "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-numbers": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
+      "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/floating-point-hex-parser": "1.13.2",
+        "@webassemblyjs/helper-api-error": "1.13.2",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
+      "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/helper-wasm-section": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
+      "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-buffer": "1.14.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/wasm-gen": "1.14.1"
+      }
+    },
+    "node_modules/@webassemblyjs/ieee754": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
+      "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@xtuc/ieee754": "^1.2.0"
+      }
+    },
+    "node_modules/@webassemblyjs/leb128": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
+      "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@webassemblyjs/utf8": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
+      "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@webassemblyjs/wasm-edit": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
+      "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-buffer": "1.14.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/helper-wasm-section": "1.14.1",
+        "@webassemblyjs/wasm-gen": "1.14.1",
+        "@webassemblyjs/wasm-opt": "1.14.1",
+        "@webassemblyjs/wasm-parser": "1.14.1",
+        "@webassemblyjs/wast-printer": "1.14.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-gen": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
+      "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/ieee754": "1.13.2",
+        "@webassemblyjs/leb128": "1.13.2",
+        "@webassemblyjs/utf8": "1.13.2"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-opt": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
+      "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-buffer": "1.14.1",
+        "@webassemblyjs/wasm-gen": "1.14.1",
+        "@webassemblyjs/wasm-parser": "1.14.1"
+      }
+    },
+    "node_modules/@webassemblyjs/wasm-parser": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
+      "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@webassemblyjs/helper-api-error": "1.13.2",
+        "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+        "@webassemblyjs/ieee754": "1.13.2",
+        "@webassemblyjs/leb128": "1.13.2",
+        "@webassemblyjs/utf8": "1.13.2"
+      }
+    },
+    "node_modules/@webassemblyjs/wast-printer": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
+      "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@webassemblyjs/ast": "1.14.1",
+        "@xtuc/long": "4.2.2"
+      }
+    },
+    "node_modules/@xtuc/ieee754": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@xtuc/long": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz",
+      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/accepts": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/accepts/-/accepts-2.0.0.tgz",
+      "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-types": "^3.0.0",
+        "negotiator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "8.15.0",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz",
+      "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-import-phases": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz",
+      "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "peerDependencies": {
+        "acorn": "^8.14.0"
+      }
+    },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
+    "node_modules/acorn-walk": {
+      "version": "8.3.4",
+      "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.4.tgz",
+      "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+      "devOptional": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.11.0"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/agent-base": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-7.1.4.tgz",
+      "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ajv-formats": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz",
+      "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "ajv": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/ajv-keywords": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+      "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3"
+      },
+      "peerDependencies": {
+        "ajv": "^8.8.2"
+      }
+    },
+    "node_modules/ansi-colors": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.3.tgz",
+      "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/ansi-escapes": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+      "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "type-fest": "^0.21.3"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ansi-escapes/node_modules/type-fest": {
+      "version": "0.21.3",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz",
+      "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/ansis": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/ansis/-/ansis-4.2.0.tgz",
+      "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/any-promise": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+      "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/anymatch/node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/apache-arrow": {
+      "version": "21.1.0",
+      "resolved": "https://registry.npmmirror.com/apache-arrow/-/apache-arrow-21.1.0.tgz",
+      "integrity": "sha512-kQrYLxhC+NTVVZ4CCzGF6L/uPVOzJmD1T3XgbiUnP7oTeVFOFgEUu6IKNwCDkpFoBVqDKQivlX4RUFqqnWFlEA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@swc/helpers": "^0.5.11",
+        "@types/command-line-args": "^5.2.3",
+        "@types/command-line-usage": "^5.0.4",
+        "@types/node": "^24.0.3",
+        "command-line-args": "^6.0.1",
+        "command-line-usage": "^7.0.1",
+        "flatbuffers": "^25.1.24",
+        "json-bignum": "^0.0.3",
+        "tslib": "^2.6.2"
+      },
+      "bin": {
+        "arrow2csv": "bin/arrow2csv.js"
+      }
+    },
+    "node_modules/apache-arrow/node_modules/@types/node": {
+      "version": "24.10.3",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-24.10.3.tgz",
+      "integrity": "sha512-gqkrWUsS8hcm0r44yn7/xZeV1ERva/nLgrLxFRUGb7aoNMIJfZJ3AC261zDQuOAKC7MiXai1WCpYc48jAHoShQ==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~7.16.0"
+      }
+    },
+    "node_modules/app-root-path": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/app-root-path/-/app-root-path-3.1.0.tgz",
+      "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6.0.0"
+      }
+    },
+    "node_modules/append-field": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/append-field/-/append-field-1.0.0.tgz",
+      "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
+      "license": "MIT"
+    },
+    "node_modules/arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true,
+      "license": "Python-2.0"
+    },
+    "node_modules/array-back": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmmirror.com/array-back/-/array-back-6.2.2.tgz",
+      "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.17"
+      }
+    },
+    "node_modules/array-timsort": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/array-timsort/-/array-timsort-1.0.3.tgz",
+      "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/asap": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmmirror.com/asap/-/asap-2.0.6.tgz",
+      "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/autoprefixer": {
+      "version": "10.4.23",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
+      "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "browserslist": "^4.28.1",
+        "caniuse-lite": "^1.0.30001760",
+        "fraction.js": "^5.3.4",
+        "picocolors": "^1.1.1",
+        "postcss-value-parser": "^4.2.0"
+      },
+      "bin": {
+        "autoprefixer": "bin/autoprefixer"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      },
+      "peerDependencies": {
+        "postcss": "^8.1.0"
+      }
+    },
+    "node_modules/available-typed-arrays": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+      "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+      "license": "MIT",
+      "dependencies": {
+        "possible-typed-array-names": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/axios": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.13.2.tgz",
+      "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.4",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/babel-jest": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/babel-jest/-/babel-jest-30.2.0.tgz",
+      "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/transform": "30.2.0",
+        "@types/babel__core": "^7.20.5",
+        "babel-plugin-istanbul": "^7.0.1",
+        "babel-preset-jest": "30.2.0",
+        "chalk": "^4.1.2",
+        "graceful-fs": "^4.2.11",
+        "slash": "^3.0.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.11.0 || ^8.0.0-0"
+      }
+    },
+    "node_modules/babel-plugin-istanbul": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz",
+      "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "workspaces": [
+        "test/babel-8"
+      ],
+      "dependencies": {
+        "@babel/helper-plugin-utils": "^7.0.0",
+        "@istanbuljs/load-nyc-config": "^1.0.0",
+        "@istanbuljs/schema": "^0.1.3",
+        "istanbul-lib-instrument": "^6.0.2",
+        "test-exclude": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/babel-plugin-jest-hoist": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz",
+      "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/babel__core": "^7.20.5"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/babel-preset-current-node-syntax": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz",
+      "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/plugin-syntax-async-generators": "^7.8.4",
+        "@babel/plugin-syntax-bigint": "^7.8.3",
+        "@babel/plugin-syntax-class-properties": "^7.12.13",
+        "@babel/plugin-syntax-class-static-block": "^7.14.5",
+        "@babel/plugin-syntax-import-attributes": "^7.24.7",
+        "@babel/plugin-syntax-import-meta": "^7.10.4",
+        "@babel/plugin-syntax-json-strings": "^7.8.3",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+        "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+        "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+        "@babel/plugin-syntax-top-level-await": "^7.14.5"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0 || ^8.0.0-0"
+      }
+    },
+    "node_modules/babel-preset-jest": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz",
+      "integrity": "sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "babel-plugin-jest-hoist": "30.2.0",
+        "babel-preset-current-node-syntax": "^1.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.11.0 || ^8.0.0-beta.1"
+      }
+    },
+    "node_modules/bail": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/bail/-/bail-2.0.2.tgz",
+      "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "license": "MIT"
+    },
+    "node_modules/base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/baseline-browser-mapping": {
+      "version": "2.9.6",
+      "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.6.tgz",
+      "integrity": "sha512-v9BVVpOTLB59C9E7aSnmIF8h7qRsFpx+A2nugVMTszEOMcfjlZMsXRm4LF23I3Z9AJxc8ANpIvzbzONoX9VJlg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "baseline-browser-mapping": "dist/cli.js"
+      }
+    },
+    "node_modules/bcrypt": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/bcrypt/-/bcrypt-6.0.0.tgz",
+      "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "node-addon-api": "^8.3.0",
+        "node-gyp-build": "^4.8.4"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/better-sqlite3": {
+      "version": "12.5.0",
+      "resolved": "https://registry.npmmirror.com/better-sqlite3/-/better-sqlite3-12.5.0.tgz",
+      "integrity": "sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "bindings": "^1.5.0",
+        "prebuild-install": "^7.1.1"
+      },
+      "engines": {
+        "node": "20.x || 22.x || 23.x || 24.x || 25.x"
+      }
+    },
+    "node_modules/bignumber.js": {
+      "version": "9.3.1",
+      "resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.3.1.tgz",
+      "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "license": "MIT",
+      "dependencies": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
+    "node_modules/bl": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz",
+      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+      "license": "MIT",
+      "dependencies": {
+        "buffer": "^5.5.0",
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.4.0"
+      }
+    },
+    "node_modules/bl/node_modules/buffer": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
+      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.1.13"
+      }
+    },
+    "node_modules/bmp-js": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
+      "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==",
+      "license": "MIT"
+    },
+    "node_modules/body-parser": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-2.2.1.tgz",
+      "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "^3.1.2",
+        "content-type": "^1.0.5",
+        "debug": "^4.4.3",
+        "http-errors": "^2.0.0",
+        "iconv-lite": "^0.7.0",
+        "on-finished": "^2.4.1",
+        "qs": "^6.14.0",
+        "raw-body": "^3.0.1",
+        "type-is": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.12",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz",
+      "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz",
+      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fill-range": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.28.1",
+      "resolved": "https://registry.npmmirror.com/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",
+      "peer": true,
+      "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/bs-logger": {
+      "version": "0.2.6",
+      "resolved": "https://registry.npmmirror.com/bs-logger/-/bs-logger-0.2.6.tgz",
+      "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-json-stable-stringify": "2.x"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/bser": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/bser/-/bser-2.1.1.tgz",
+      "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "node-int64": "^0.4.0"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "node_modules/buffer-equal-constant-time": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+      "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "license": "MIT"
+    },
+    "node_modules/busboy": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz",
+      "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+      "dependencies": {
+        "streamsearch": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=10.16.0"
+      }
+    },
+    "node_modules/bytes": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz",
+      "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/call-bind": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.8.tgz",
+      "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.0",
+        "es-define-property": "^1.0.0",
+        "get-intrinsic": "^1.2.4",
+        "set-function-length": "^1.2.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/call-bound": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz",
+      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "get-intrinsic": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/camelcase": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-6.3.0.tgz",
+      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/camelcase-css": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+      "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001760",
+      "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz",
+      "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==",
+      "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/ccount": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/ccount/-/ccount-2.0.1.tgz",
+      "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "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/chalk-template": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/chalk-template/-/chalk-template-0.4.0.tgz",
+      "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==",
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk-template?sponsor=1"
+      }
+    },
+    "node_modules/chalk/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/char-regex": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/char-regex/-/char-regex-1.0.2.tgz",
+      "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/character-entities": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/character-entities/-/character-entities-2.0.2.tgz",
+      "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/character-entities-html4": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+      "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/character-entities-legacy": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+      "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/character-reference-invalid": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+      "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/chardet": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/chardet/-/chardet-2.1.1.tgz",
+      "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/chevrotain": {
+      "version": "11.0.3",
+      "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz",
+      "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@chevrotain/cst-dts-gen": "11.0.3",
+        "@chevrotain/gast": "11.0.3",
+        "@chevrotain/regexp-to-ast": "11.0.3",
+        "@chevrotain/types": "11.0.3",
+        "@chevrotain/utils": "11.0.3",
+        "lodash-es": "4.17.21"
+      }
+    },
+    "node_modules/chevrotain-allstar": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz",
+      "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==",
+      "license": "MIT",
+      "dependencies": {
+        "lodash-es": "^4.17.21"
+      },
+      "peerDependencies": {
+        "chevrotain": "^11.0.0"
+      }
+    },
+    "node_modules/chevrotain/node_modules/lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
+      "license": "MIT"
+    },
+    "node_modules/chokidar": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz",
+      "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "readdirp": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 14.16.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/chownr": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz",
+      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+      "license": "ISC"
+    },
+    "node_modules/chrome-trace-event": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz",
+      "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/ci-info": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-4.3.1.tgz",
+      "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/sibiraj-s"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cjs-module-lexer": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz",
+      "integrity": "sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/class-transformer": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmmirror.com/class-transformer/-/class-transformer-0.5.1.tgz",
+      "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==",
+      "license": "MIT",
+      "peer": true
+    },
+    "node_modules/class-validator": {
+      "version": "0.14.3",
+      "resolved": "https://registry.npmmirror.com/class-validator/-/class-validator-0.14.3.tgz",
+      "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/validator": "^13.15.3",
+        "libphonenumber-js": "^1.11.1",
+        "validator": "^13.15.20"
+      }
+    },
+    "node_modules/cli-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz",
+      "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "restore-cursor": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cli-spinners": {
+      "version": "2.9.2",
+      "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz",
+      "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-table3": {
+      "version": "0.6.5",
+      "resolved": "https://registry.npmmirror.com/cli-table3/-/cli-table3-0.6.5.tgz",
+      "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "string-width": "^4.2.0"
+      },
+      "engines": {
+        "node": "10.* || >= 12.*"
+      },
+      "optionalDependencies": {
+        "@colors/colors": "1.5.0"
+      }
+    },
+    "node_modules/cli-width": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/cli-width/-/cli-width-4.1.0.tgz",
+      "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/clone": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz",
+      "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/co": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmmirror.com/co/-/co-4.6.0.tgz",
+      "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">= 1.0.0",
+        "node": ">= 0.12.0"
+      }
+    },
+    "node_modules/collect-v8-coverage": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz",
+      "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "license": "MIT"
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/comma-separated-tokens": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+      "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/command-line-args": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/command-line-args/-/command-line-args-6.0.1.tgz",
+      "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==",
+      "license": "MIT",
+      "dependencies": {
+        "array-back": "^6.2.2",
+        "find-replace": "^5.0.2",
+        "lodash.camelcase": "^4.3.0",
+        "typical": "^7.2.0"
+      },
+      "engines": {
+        "node": ">=12.20"
+      },
+      "peerDependencies": {
+        "@75lb/nature": "latest"
+      },
+      "peerDependenciesMeta": {
+        "@75lb/nature": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/command-line-usage": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmmirror.com/command-line-usage/-/command-line-usage-7.0.3.tgz",
+      "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==",
+      "license": "MIT",
+      "dependencies": {
+        "array-back": "^6.2.2",
+        "chalk-template": "^0.4.0",
+        "table-layout": "^4.1.0",
+        "typical": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=12.20.0"
+      }
+    },
+    "node_modules/commander": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz",
+      "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/comment-json": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmmirror.com/comment-json/-/comment-json-4.4.1.tgz",
+      "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "array-timsort": "^1.0.3",
+        "core-util-is": "^1.0.3",
+        "esprima": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/component-emitter": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/component-emitter/-/component-emitter-1.3.1.tgz",
+      "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/concat-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/concat-stream/-/concat-stream-2.0.0.tgz",
+      "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
+      "engines": [
+        "node >= 6.0"
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^3.0.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "node_modules/concurrently": {
+      "version": "8.2.2",
+      "resolved": "https://registry.npmmirror.com/concurrently/-/concurrently-8.2.2.tgz",
+      "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.2",
+        "date-fns": "^2.30.0",
+        "lodash": "^4.17.21",
+        "rxjs": "^7.8.1",
+        "shell-quote": "^1.8.1",
+        "spawn-command": "0.0.2",
+        "supports-color": "^8.1.1",
+        "tree-kill": "^1.2.2",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "conc": "dist/bin/concurrently.js",
+        "concurrently": "dist/bin/concurrently.js"
+      },
+      "engines": {
+        "node": "^14.13.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
+      }
+    },
+    "node_modules/confbox": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
+      "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+      "license": "MIT"
+    },
+    "node_modules/consola": {
+      "version": "3.4.2",
+      "resolved": "https://registry.npmmirror.com/consola/-/consola-3.4.2.tgz",
+      "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==",
+      "license": "MIT",
+      "engines": {
+        "node": "^14.18.0 || >=16.10.0"
+      }
+    },
+    "node_modules/console-table-printer": {
+      "version": "2.15.0",
+      "resolved": "https://registry.npmmirror.com/console-table-printer/-/console-table-printer-2.15.0.tgz",
+      "integrity": "sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==",
+      "license": "MIT",
+      "dependencies": {
+        "simple-wcswidth": "^1.1.2"
+      }
+    },
+    "node_modules/content-disposition": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-1.0.1.tgz",
+      "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/content-type": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz",
+      "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cookie": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz",
+      "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/cookie-signature": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.2.2.tgz",
+      "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.6.0"
+      }
+    },
+    "node_modules/cookiejar": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmmirror.com/cookiejar/-/cookiejar-2.1.4.tgz",
+      "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cors": {
+      "version": "2.8.5",
+      "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz",
+      "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+      "license": "MIT",
+      "dependencies": {
+        "object-assign": "^4",
+        "vary": "^1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/cose-base": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz",
+      "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==",
+      "license": "MIT",
+      "dependencies": {
+        "layout-base": "^1.0.0"
+      }
+    },
+    "node_modules/cosmiconfig": {
+      "version": "8.3.6",
+      "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz",
+      "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "import-fresh": "^3.3.0",
+        "js-yaml": "^4.1.0",
+        "parse-json": "^5.2.0",
+        "path-type": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/d-fischer"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.9.5"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/create-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/create-require/-/create-require-1.1.1.tgz",
+      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/cron": {
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/cron/-/cron-4.3.5.tgz",
+      "integrity": "sha512-hKPP7fq1+OfyCqoePkKfVq7tNAdFwiQORr4lZUHwrf0tebC65fYEeWgOrXOL6prn1/fegGOdTfrM6e34PJfksg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/luxon": "~3.7.0",
+        "luxon": "~3.7.0"
+      },
+      "engines": {
+        "node": ">=18.x"
+      }
+    },
+    "node_modules/cross-spawn": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
+      "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/css-line-break": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
+      "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
+      "license": "MIT",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
+    "node_modules/cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "cssesc": "bin/cssesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "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==",
+      "license": "MIT"
+    },
+    "node_modules/cytoscape": {
+      "version": "3.33.1",
+      "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz",
+      "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/cytoscape-cose-bilkent": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz",
+      "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==",
+      "license": "MIT",
+      "dependencies": {
+        "cose-base": "^1.0.0"
+      },
+      "peerDependencies": {
+        "cytoscape": "^3.2.0"
+      }
+    },
+    "node_modules/cytoscape-fcose": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz",
+      "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==",
+      "license": "MIT",
+      "dependencies": {
+        "cose-base": "^2.2.0"
+      },
+      "peerDependencies": {
+        "cytoscape": "^3.2.0"
+      }
+    },
+    "node_modules/cytoscape-fcose/node_modules/cose-base": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz",
+      "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==",
+      "license": "MIT",
+      "dependencies": {
+        "layout-base": "^2.0.0"
+      }
+    },
+    "node_modules/cytoscape-fcose/node_modules/layout-base": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz",
+      "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==",
+      "license": "MIT"
+    },
+    "node_modules/d3": {
+      "version": "7.9.0",
+      "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz",
+      "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-array": "3",
+        "d3-axis": "3",
+        "d3-brush": "3",
+        "d3-chord": "3",
+        "d3-color": "3",
+        "d3-contour": "4",
+        "d3-delaunay": "6",
+        "d3-dispatch": "3",
+        "d3-drag": "3",
+        "d3-dsv": "3",
+        "d3-ease": "3",
+        "d3-fetch": "3",
+        "d3-force": "3",
+        "d3-format": "3",
+        "d3-geo": "3",
+        "d3-hierarchy": "3",
+        "d3-interpolate": "3",
+        "d3-path": "3",
+        "d3-polygon": "3",
+        "d3-quadtree": "3",
+        "d3-random": "3",
+        "d3-scale": "4",
+        "d3-scale-chromatic": "3",
+        "d3-selection": "3",
+        "d3-shape": "3",
+        "d3-time": "3",
+        "d3-time-format": "4",
+        "d3-timer": "3",
+        "d3-transition": "3",
+        "d3-zoom": "3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-array": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+      "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+      "license": "ISC",
+      "dependencies": {
+        "internmap": "1 - 2"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-axis": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz",
+      "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-brush": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz",
+      "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-drag": "2 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-selection": "3",
+        "d3-transition": "3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-chord": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz",
+      "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-path": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-color": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+      "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-contour": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz",
+      "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-array": "^3.2.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-delaunay": {
+      "version": "6.0.4",
+      "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
+      "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
+      "license": "ISC",
+      "dependencies": {
+        "delaunator": "5"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-dispatch": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
+      "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-drag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
+      "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-selection": "3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-dsv": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz",
+      "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==",
+      "license": "ISC",
+      "dependencies": {
+        "commander": "7",
+        "iconv-lite": "0.6",
+        "rw": "1"
+      },
+      "bin": {
+        "csv2json": "bin/dsv2json.js",
+        "csv2tsv": "bin/dsv2dsv.js",
+        "dsv2dsv": "bin/dsv2dsv.js",
+        "dsv2json": "bin/dsv2json.js",
+        "json2csv": "bin/json2dsv.js",
+        "json2dsv": "bin/json2dsv.js",
+        "json2tsv": "bin/json2dsv.js",
+        "tsv2csv": "bin/dsv2dsv.js",
+        "tsv2json": "bin/dsv2json.js"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-dsv/node_modules/commander": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/d3-dsv/node_modules/iconv-lite": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+      "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/d3-ease": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+      "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-fetch": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz",
+      "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dsv": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-force": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz",
+      "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-quadtree": "1 - 3",
+        "d3-timer": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-format": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
+      "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-geo": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz",
+      "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-array": "2.5.0 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-hierarchy": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz",
+      "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-interpolate": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+      "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-color": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-path": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+      "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-polygon": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz",
+      "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-quadtree": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz",
+      "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-random": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz",
+      "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-sankey": {
+      "version": "0.12.3",
+      "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
+      "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "d3-array": "1 - 2",
+        "d3-shape": "^1.2.0"
+      }
+    },
+    "node_modules/d3-sankey/node_modules/d3-array": {
+      "version": "2.12.1",
+      "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+      "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "internmap": "^1.0.0"
+      }
+    },
+    "node_modules/d3-sankey/node_modules/d3-path": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+      "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/d3-sankey/node_modules/d3-shape": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+      "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "d3-path": "1"
+      }
+    },
+    "node_modules/d3-sankey/node_modules/internmap": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+      "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",
+      "license": "ISC"
+    },
+    "node_modules/d3-scale": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+      "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-array": "2.10.0 - 3",
+        "d3-format": "1 - 3",
+        "d3-interpolate": "1.2.0 - 3",
+        "d3-time": "2.1.1 - 3",
+        "d3-time-format": "2 - 4"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-scale-chromatic": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz",
+      "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-color": "1 - 3",
+        "d3-interpolate": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-selection": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
+      "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
+      "license": "ISC",
+      "peer": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-shape": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+      "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-path": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-time": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+      "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-array": "2 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-time-format": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+      "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-time": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-timer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+      "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/d3-transition": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
+      "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-color": "1 - 3",
+        "d3-dispatch": "1 - 3",
+        "d3-ease": "1 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-timer": "1 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "d3-selection": "2 - 3"
+      }
+    },
+    "node_modules/d3-zoom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
+      "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
+      "license": "ISC",
+      "dependencies": {
+        "d3-dispatch": "1 - 3",
+        "d3-drag": "2 - 3",
+        "d3-interpolate": "1 - 3",
+        "d3-selection": "2 - 3",
+        "d3-transition": "2 - 3"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/dagre-d3-es": {
+      "version": "7.0.13",
+      "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz",
+      "integrity": "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==",
+      "license": "MIT",
+      "dependencies": {
+        "d3": "^7.9.0",
+        "lodash-es": "^4.17.21"
+      }
+    },
+    "node_modules/data-uri-to-buffer": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+      "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/date-fns": {
+      "version": "2.30.0",
+      "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-2.30.0.tgz",
+      "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.21.0"
+      },
+      "engines": {
+        "node": ">=0.11"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/date-fns"
+      }
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.19",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz",
+      "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
+      "license": "MIT"
+    },
+    "node_modules/debug": {
+      "version": "4.4.3",
+      "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
+      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/decode-named-character-reference": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
+      "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
+      "license": "MIT",
+      "dependencies": {
+        "character-entities": "^2.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/decompress-response": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz",
+      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "license": "MIT",
+      "dependencies": {
+        "mimic-response": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/dedent": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmmirror.com/dedent/-/dedent-1.7.0.tgz",
+      "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "babel-plugin-macros": "^3.1.0"
+      },
+      "peerDependenciesMeta": {
+        "babel-plugin-macros": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/deep-extend": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz",
+      "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/deepmerge": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz",
+      "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/defaults": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz",
+      "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "clone": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/define-data-property": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz",
+      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+      "license": "MIT",
+      "dependencies": {
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/delaunator": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
+      "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
+      "license": "ISC",
+      "dependencies": {
+        "robust-predicates": "^3.0.2"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/dequal": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz",
+      "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
+      "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/detect-newline": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/detect-newline/-/detect-newline-3.1.0.tgz",
+      "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/devlop": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/devlop/-/devlop-1.1.0.tgz",
+      "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+      "license": "MIT",
+      "dependencies": {
+        "dequal": "^2.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/dezalgo": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmmirror.com/dezalgo/-/dezalgo-1.0.4.tgz",
+      "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "asap": "^2.0.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/didyoumean": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+      "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "devOptional": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.3.1"
+      }
+    },
+    "node_modules/dlv": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/dompurify": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz",
+      "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==",
+      "license": "(MPL-2.0 OR Apache-2.0)",
+      "optionalDependencies": {
+        "@types/trusted-types": "^2.0.7"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "16.6.1",
+      "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz",
+      "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/dotenv-expand": {
+      "version": "12.0.1",
+      "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-12.0.1.tgz",
+      "integrity": "sha512-LaKRbou8gt0RNID/9RoI+J2rvXsBRPMV7p+ElHlPhcSARbCPDYcYG2s1TIzAfWv4YSgyY5taidWzzs31lNV3yQ==",
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "dotenv": "^16.4.5"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/eastasianwidth": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+      "license": "MIT"
+    },
+    "node_modules/ecdsa-sig-formatter": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+      "license": "MIT"
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.5.267",
+      "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+      "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/emittery": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmmirror.com/emittery/-/emittery-0.13.1.tgz",
+      "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/emittery?sponsor=1"
+      }
+    },
+    "node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "license": "MIT"
+    },
+    "node_modules/encodeurl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-2.0.0.tgz",
+      "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/end-of-stream": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.5.tgz",
+      "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+      "license": "MIT",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/enhanced-resolve": {
+      "version": "5.18.3",
+      "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz",
+      "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/entities": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+      "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.4.tgz",
+      "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-module-lexer": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+      "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.25.12",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.12.tgz",
+      "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.25.12",
+        "@esbuild/android-arm": "0.25.12",
+        "@esbuild/android-arm64": "0.25.12",
+        "@esbuild/android-x64": "0.25.12",
+        "@esbuild/darwin-arm64": "0.25.12",
+        "@esbuild/darwin-x64": "0.25.12",
+        "@esbuild/freebsd-arm64": "0.25.12",
+        "@esbuild/freebsd-x64": "0.25.12",
+        "@esbuild/linux-arm": "0.25.12",
+        "@esbuild/linux-arm64": "0.25.12",
+        "@esbuild/linux-ia32": "0.25.12",
+        "@esbuild/linux-loong64": "0.25.12",
+        "@esbuild/linux-mips64el": "0.25.12",
+        "@esbuild/linux-ppc64": "0.25.12",
+        "@esbuild/linux-riscv64": "0.25.12",
+        "@esbuild/linux-s390x": "0.25.12",
+        "@esbuild/linux-x64": "0.25.12",
+        "@esbuild/netbsd-arm64": "0.25.12",
+        "@esbuild/netbsd-x64": "0.25.12",
+        "@esbuild/openbsd-arm64": "0.25.12",
+        "@esbuild/openbsd-x64": "0.25.12",
+        "@esbuild/openharmony-arm64": "0.25.12",
+        "@esbuild/sunos-x64": "0.25.12",
+        "@esbuild/win32-arm64": "0.25.12",
+        "@esbuild/win32-ia32": "0.25.12",
+        "@esbuild/win32-x64": "0.25.12"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+      "license": "MIT"
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/eslint": {
+      "version": "9.39.1",
+      "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.39.1.tgz",
+      "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@eslint-community/eslint-utils": "^4.8.0",
+        "@eslint-community/regexpp": "^4.12.1",
+        "@eslint/config-array": "^0.21.1",
+        "@eslint/config-helpers": "^0.4.2",
+        "@eslint/core": "^0.17.0",
+        "@eslint/eslintrc": "^3.3.1",
+        "@eslint/js": "9.39.1",
+        "@eslint/plugin-kit": "^0.4.1",
+        "@humanfs/node": "^0.16.6",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@humanwhocodes/retry": "^0.4.2",
+        "@types/estree": "^1.0.6",
+        "ajv": "^6.12.4",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.6",
+        "debug": "^4.3.2",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^8.4.0",
+        "eslint-visitor-keys": "^4.2.1",
+        "espree": "^10.4.0",
+        "esquery": "^1.5.0",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^8.0.0",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3"
+      },
+      "bin": {
+        "eslint": "bin/eslint.js"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://eslint.org/donate"
+      },
+      "peerDependencies": {
+        "jiti": "*"
+      },
+      "peerDependenciesMeta": {
+        "jiti": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-config-prettier": {
+      "version": "10.1.8",
+      "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
+      "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "eslint-config-prettier": "bin/cli.js"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint-config-prettier"
+      },
+      "peerDependencies": {
+        "eslint": ">=7.0.0"
+      }
+    },
+    "node_modules/eslint-plugin-prettier": {
+      "version": "5.5.4",
+      "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz",
+      "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prettier-linter-helpers": "^1.0.0",
+        "synckit": "^0.11.7"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint-plugin-prettier"
+      },
+      "peerDependencies": {
+        "@types/eslint": ">=8.0.0",
+        "eslint": ">=8.0.0",
+        "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
+        "prettier": ">=3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/eslint": {
+          "optional": true
+        },
+        "eslint-config-prettier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/eslint-scope": {
+      "version": "8.4.0",
+      "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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/eslint/node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmmirror.com/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/eslint/node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/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/eslint/node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/eslint/node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/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/eslint/node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/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/espree": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz",
+      "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^4.2.1"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/eslint"
+      }
+    },
+    "node_modules/esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "bin": {
+        "esparse": "bin/esparse.js",
+        "esvalidate": "bin/esvalidate.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/esquery": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz",
+      "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "estraverse": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/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.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/estree-util-is-identifier-name": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+      "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "license": "MIT"
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
+    "node_modules/execa": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz",
+      "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.0",
+        "human-signals": "^2.1.0",
+        "is-stream": "^2.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^4.0.1",
+        "onetime": "^5.1.2",
+        "signal-exit": "^3.0.3",
+        "strip-final-newline": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/execa/node_modules/signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/exit-x": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmmirror.com/exit-x/-/exit-x-0.2.2.tgz",
+      "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/expand-template": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz",
+      "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==",
+      "license": "(MIT OR WTFPL)",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/expect": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/expect/-/expect-30.2.0.tgz",
+      "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/expect-utils": "30.2.0",
+        "@jest/get-type": "30.1.0",
+        "jest-matcher-utils": "30.2.0",
+        "jest-message-util": "30.2.0",
+        "jest-mock": "30.2.0",
+        "jest-util": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/express": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/express/-/express-5.1.0.tgz",
+      "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "accepts": "^2.0.0",
+        "body-parser": "^2.2.0",
+        "content-disposition": "^1.0.0",
+        "content-type": "^1.0.5",
+        "cookie": "^0.7.1",
+        "cookie-signature": "^1.2.1",
+        "debug": "^4.4.0",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "etag": "^1.8.1",
+        "finalhandler": "^2.1.0",
+        "fresh": "^2.0.0",
+        "http-errors": "^2.0.0",
+        "merge-descriptors": "^2.0.0",
+        "mime-types": "^3.0.0",
+        "on-finished": "^2.4.1",
+        "once": "^1.4.0",
+        "parseurl": "^1.3.3",
+        "proxy-addr": "^2.0.7",
+        "qs": "^6.14.0",
+        "range-parser": "^1.2.1",
+        "router": "^2.2.0",
+        "send": "^1.1.0",
+        "serve-static": "^2.2.0",
+        "statuses": "^2.0.1",
+        "type-is": "^2.0.1",
+        "vary": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "license": "MIT"
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-diff": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz",
+      "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/fast-glob": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+      "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.8"
+      },
+      "engines": {
+        "node": ">=8.6.0"
+      }
+    },
+    "node_modules/fast-glob/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/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.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fast-safe-stringify": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+      "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+      "license": "MIT"
+    },
+    "node_modules/fast-uri": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.0.tgz",
+      "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/fastq": {
+      "version": "1.20.1",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+      "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "node_modules/fault": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz",
+      "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==",
+      "license": "MIT",
+      "dependencies": {
+        "format": "^0.2.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/fb-watchman": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/fb-watchman/-/fb-watchman-2.0.2.tgz",
+      "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bser": "2.1.1"
+      }
+    },
+    "node_modules/fdir": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmmirror.com/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/fetch-blob": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/fetch-blob/-/fetch-blob-3.2.0.tgz",
+      "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "paypal",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "node-domexception": "^1.0.0",
+        "web-streams-polyfill": "^3.0.3"
+      },
+      "engines": {
+        "node": "^12.20 || >= 14.13"
+      }
+    },
+    "node_modules/fflate": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz",
+      "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
+      "license": "MIT"
+    },
+    "node_modules/file-entry-cache": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+      "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "flat-cache": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/file-type": {
+      "version": "21.1.0",
+      "resolved": "https://registry.npmmirror.com/file-type/-/file-type-21.1.0.tgz",
+      "integrity": "sha512-boU4EHmP3JXkwDo4uhyBhTt5pPstxB6eEXKJBu2yu2l7aAMMm7QQYQEzssJmKReZYrFdFOJS8koVo6bXIBGDqA==",
+      "license": "MIT",
+      "dependencies": {
+        "@tokenizer/inflate": "^0.3.1",
+        "strtok3": "^10.3.1",
+        "token-types": "^6.0.0",
+        "uint8array-extras": "^1.4.0"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/file-type?sponsor=1"
+      }
+    },
+    "node_modules/file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+      "license": "MIT"
+    },
+    "node_modules/fill-range": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz",
+      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/finalhandler": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-2.1.1.tgz",
+      "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.4.0",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "on-finished": "^2.4.1",
+        "parseurl": "^1.3.3",
+        "statuses": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 18.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/find-replace": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmmirror.com/find-replace/-/find-replace-5.0.2.tgz",
+      "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@75lb/nature": "latest"
+      },
+      "peerDependenciesMeta": {
+        "@75lb/nature": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/find-up": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
+      "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "locate-path": "^5.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/flat-cache": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/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/flatbuffers": {
+      "version": "25.9.23",
+      "resolved": "https://registry.npmmirror.com/flatbuffers/-/flatbuffers-25.9.23.tgz",
+      "integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/flatted": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz",
+      "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.11",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz",
+      "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/for-each": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz",
+      "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+      "license": "MIT",
+      "dependencies": {
+        "is-callable": "^1.2.7"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/foreground-child": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz",
+      "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+      "license": "ISC",
+      "dependencies": {
+        "cross-spawn": "^7.0.6",
+        "signal-exit": "^4.0.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/fork-ts-checker-webpack-plugin": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmmirror.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz",
+      "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.16.7",
+        "chalk": "^4.1.2",
+        "chokidar": "^4.0.1",
+        "cosmiconfig": "^8.2.0",
+        "deepmerge": "^4.2.2",
+        "fs-extra": "^10.0.0",
+        "memfs": "^3.4.1",
+        "minimatch": "^3.0.4",
+        "node-abort-controller": "^3.0.1",
+        "schema-utils": "^3.1.1",
+        "semver": "^7.3.5",
+        "tapable": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=14.21.3"
+      },
+      "peerDependencies": {
+        "typescript": ">3.6.0",
+        "webpack": "^5.11.0"
+      }
+    },
+    "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+      "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+      "dev": true,
+      "license": "MIT",
+      "peerDependencies": {
+        "ajv": "^6.9.1"
+      }
+    },
+    "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz",
+      "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.8",
+        "ajv": "^6.12.5",
+        "ajv-keywords": "^3.5.2"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
+      "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "hasown": "^2.0.2",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/form-data/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/form-data/node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/format": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz",
+      "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==",
+      "engines": {
+        "node": ">=0.4.x"
+      }
+    },
+    "node_modules/formdata-polyfill": {
+      "version": "4.0.10",
+      "resolved": "https://registry.npmmirror.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+      "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+      "license": "MIT",
+      "dependencies": {
+        "fetch-blob": "^3.1.2"
+      },
+      "engines": {
+        "node": ">=12.20.0"
+      }
+    },
+    "node_modules/formidable": {
+      "version": "3.5.4",
+      "resolved": "https://registry.npmmirror.com/formidable/-/formidable-3.5.4.tgz",
+      "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@paralleldrive/cuid2": "^2.2.2",
+        "dezalgo": "^1.0.4",
+        "once": "^1.4.0"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "funding": {
+        "url": "https://ko-fi.com/tunnckoCore/commissions"
+      }
+    },
+    "node_modules/forwarded": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
+      "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/fraction.js": {
+      "version": "5.3.4",
+      "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+      "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/rawify"
+      }
+    },
+    "node_modules/fresh": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/fresh/-/fresh-2.0.0.tgz",
+      "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/fs-constants": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz",
+      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+      "license": "MIT"
+    },
+    "node_modules/fs-extra": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz",
+      "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/fs-monkey": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.1.0.tgz",
+      "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==",
+      "dev": true,
+      "license": "Unlicense"
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/gaxios": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmmirror.com/gaxios/-/gaxios-7.1.3.tgz",
+      "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "extend": "^3.0.2",
+        "https-proxy-agent": "^7.0.1",
+        "node-fetch": "^3.3.2",
+        "rimraf": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/gcp-metadata": {
+      "version": "8.1.2",
+      "resolved": "https://registry.npmmirror.com/gcp-metadata/-/gcp-metadata-8.1.2.tgz",
+      "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "gaxios": "^7.0.0",
+        "google-logging-utils": "^1.0.0",
+        "json-bigint": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "license": "ISC",
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-package-type": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/get-package-type/-/get-package-type-0.1.0.tgz",
+      "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/github-from-package": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz",
+      "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==",
+      "license": "MIT"
+    },
+    "node_modules/glob": {
+      "version": "10.5.0",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-10.5.0.tgz",
+      "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+      "license": "ISC",
+      "dependencies": {
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^3.1.2",
+        "minimatch": "^9.0.4",
+        "minipass": "^7.1.2",
+        "package-json-from-dist": "^1.0.0",
+        "path-scurry": "^1.11.1"
+      },
+      "bin": {
+        "glob": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz",
+      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.3"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/glob-to-regexp": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+      "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+      "dev": true,
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/glob/node_modules/brace-expansion": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz",
+      "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "node_modules/glob/node_modules/minimatch": {
+      "version": "9.0.5",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz",
+      "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/globals": {
+      "version": "16.5.0",
+      "resolved": "https://registry.npmmirror.com/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/google-auth-library": {
+      "version": "10.5.0",
+      "resolved": "https://registry.npmmirror.com/google-auth-library/-/google-auth-library-10.5.0.tgz",
+      "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "ecdsa-sig-formatter": "^1.0.11",
+        "gaxios": "^7.0.0",
+        "gcp-metadata": "^8.0.0",
+        "google-logging-utils": "^1.0.0",
+        "gtoken": "^8.0.0",
+        "jws": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/google-logging-utils": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmmirror.com/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+      "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/gtoken": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/gtoken/-/gtoken-8.0.0.tgz",
+      "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==",
+      "license": "MIT",
+      "dependencies": {
+        "gaxios": "^7.0.0",
+        "jws": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/hachure-fill": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",
+      "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==",
+      "license": "MIT"
+    },
+    "node_modules/handlebars": {
+      "version": "4.7.8",
+      "resolved": "https://registry.npmmirror.com/handlebars/-/handlebars-4.7.8.tgz",
+      "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "^1.2.5",
+        "neo-async": "^2.6.2",
+        "source-map": "^0.6.1",
+        "wordwrap": "^1.0.0"
+      },
+      "bin": {
+        "handlebars": "bin/handlebars"
+      },
+      "engines": {
+        "node": ">=0.4.7"
+      },
+      "optionalDependencies": {
+        "uglify-js": "^3.1.4"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/has-property-descriptors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+      "license": "MIT",
+      "dependencies": {
+        "es-define-property": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/hast-util-from-dom": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz",
+      "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==",
+      "license": "ISC",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "hastscript": "^9.0.0",
+        "web-namespaces": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-from-html": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz",
+      "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "devlop": "^1.1.0",
+        "hast-util-from-parse5": "^8.0.0",
+        "parse5": "^7.0.0",
+        "vfile": "^6.0.0",
+        "vfile-message": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-from-html-isomorphic": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz",
+      "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "hast-util-from-dom": "^5.0.0",
+        "hast-util-from-html": "^2.0.0",
+        "unist-util-remove-position": "^5.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-from-parse5": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz",
+      "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/unist": "^3.0.0",
+        "devlop": "^1.0.0",
+        "hastscript": "^9.0.0",
+        "property-information": "^7.0.0",
+        "vfile": "^6.0.0",
+        "vfile-location": "^5.0.0",
+        "web-namespaces": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-is-element": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz",
+      "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-parse-selector": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
+      "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-to-jsx-runtime": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmmirror.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
+      "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0",
+        "@types/hast": "^3.0.0",
+        "@types/unist": "^3.0.0",
+        "comma-separated-tokens": "^2.0.0",
+        "devlop": "^1.0.0",
+        "estree-util-is-identifier-name": "^3.0.0",
+        "hast-util-whitespace": "^3.0.0",
+        "mdast-util-mdx-expression": "^2.0.0",
+        "mdast-util-mdx-jsx": "^3.0.0",
+        "mdast-util-mdxjs-esm": "^2.0.0",
+        "property-information": "^7.0.0",
+        "space-separated-tokens": "^2.0.0",
+        "style-to-js": "^1.0.0",
+        "unist-util-position": "^5.0.0",
+        "vfile-message": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-to-text": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz",
+      "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/unist": "^3.0.0",
+        "hast-util-is-element": "^3.0.0",
+        "unist-util-find-after": "^5.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hast-util-whitespace": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+      "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/hastscript": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
+      "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "comma-separated-tokens": "^2.0.0",
+        "hast-util-parse-selector": "^4.0.0",
+        "property-information": "^7.0.0",
+        "space-separated-tokens": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/highlight.js": {
+      "version": "10.7.3",
+      "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
+      "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/highlightjs-vue": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz",
+      "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==",
+      "license": "CC0-1.0"
+    },
+    "node_modules/hpagent": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/hpagent/-/hpagent-1.2.0.tgz",
+      "integrity": "sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/html-url-attributes": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
+      "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/html2canvas": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
+      "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
+      "license": "MIT",
+      "dependencies": {
+        "css-line-break": "^2.1.0",
+        "text-segmentation": "^1.0.3"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/http-errors": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.1.tgz",
+      "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+      "license": "MIT",
+      "dependencies": {
+        "depd": "~2.0.0",
+        "inherits": "~2.0.4",
+        "setprototypeof": "~1.2.0",
+        "statuses": "~2.0.2",
+        "toidentifier": "~1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/https-proxy-agent": {
+      "version": "7.0.6",
+      "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+      "license": "MIT",
+      "dependencies": {
+        "agent-base": "^7.1.2",
+        "debug": "4"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/human-signals": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz",
+      "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=10.17.0"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.0.tgz",
+      "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
+      "license": "MIT",
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/idb-keyval": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
+      "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/ignore": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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/import-fresh/node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/import-local": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/import-local/-/import-local-3.2.0.tgz",
+      "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pkg-dir": "^4.2.0",
+        "resolve-cwd": "^3.0.0"
+      },
+      "bin": {
+        "import-local-fixture": "fixtures/cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.19"
+      }
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/ini": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz",
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+      "license": "ISC"
+    },
+    "node_modules/inline-style-parser": {
+      "version": "0.2.7",
+      "resolved": "https://registry.npmmirror.com/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
+      "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==",
+      "license": "MIT"
+    },
+    "node_modules/internmap": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+      "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/is-alphabetical": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+      "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/is-alphanumerical": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+      "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+      "license": "MIT",
+      "dependencies": {
+        "is-alphabetical": "^2.0.0",
+        "is-decimal": "^2.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-callable": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz",
+      "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.16.1",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+      "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-decimal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/is-decimal/-/is-decimal-2.0.1.tgz",
+      "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-generator-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
+      "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-hexadecimal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+      "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/is-interactive": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz",
+      "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/is-plain-obj": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+      "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-promise": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/is-promise/-/is-promise-4.0.0.tgz",
+      "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+      "license": "MIT"
+    },
+    "node_modules/is-stream": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz",
+      "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-typed-array": {
+      "version": "1.1.15",
+      "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.15.tgz",
+      "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "which-typed-array": "^1.1.16"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-url": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+      "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
+      "license": "MIT"
+    },
+    "node_modules/isarray": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/isarray/-/isarray-2.0.5.tgz",
+      "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+      "license": "MIT"
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "license": "ISC"
+    },
+    "node_modules/istanbul-lib-coverage": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
+      "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/istanbul-lib-instrument": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
+      "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@babel/core": "^7.23.9",
+        "@babel/parser": "^7.23.9",
+        "@istanbuljs/schema": "^0.1.3",
+        "istanbul-lib-coverage": "^3.2.0",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/istanbul-lib-report": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+      "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "istanbul-lib-coverage": "^3.0.0",
+        "make-dir": "^4.0.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/istanbul-lib-report/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmmirror.com/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/istanbul-lib-source-maps": {
+      "version": "5.0.6",
+      "resolved": "https://registry.npmmirror.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz",
+      "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.23",
+        "debug": "^4.1.1",
+        "istanbul-lib-coverage": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/istanbul-reports": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
+      "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "html-escaper": "^2.0.0",
+        "istanbul-lib-report": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/iterare": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/iterare/-/iterare-1.2.1.tgz",
+      "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jackspeak": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz",
+      "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "@isaacs/cliui": "^8.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      },
+      "optionalDependencies": {
+        "@pkgjs/parseargs": "^0.11.0"
+      }
+    },
+    "node_modules/jest": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest/-/jest-30.2.0.tgz",
+      "integrity": "sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@jest/core": "30.2.0",
+        "@jest/types": "30.2.0",
+        "import-local": "^3.2.0",
+        "jest-cli": "30.2.0"
+      },
+      "bin": {
+        "jest": "bin/jest.js"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-changed-files": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-changed-files/-/jest-changed-files-30.2.0.tgz",
+      "integrity": "sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "execa": "^5.1.1",
+        "jest-util": "30.2.0",
+        "p-limit": "^3.1.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-circus": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-circus/-/jest-circus-30.2.0.tgz",
+      "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/environment": "30.2.0",
+        "@jest/expect": "30.2.0",
+        "@jest/test-result": "30.2.0",
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "chalk": "^4.1.2",
+        "co": "^4.6.0",
+        "dedent": "^1.6.0",
+        "is-generator-fn": "^2.1.0",
+        "jest-each": "30.2.0",
+        "jest-matcher-utils": "30.2.0",
+        "jest-message-util": "30.2.0",
+        "jest-runtime": "30.2.0",
+        "jest-snapshot": "30.2.0",
+        "jest-util": "30.2.0",
+        "p-limit": "^3.1.0",
+        "pretty-format": "30.2.0",
+        "pure-rand": "^7.0.0",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.6"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-cli": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-cli/-/jest-cli-30.2.0.tgz",
+      "integrity": "sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/core": "30.2.0",
+        "@jest/test-result": "30.2.0",
+        "@jest/types": "30.2.0",
+        "chalk": "^4.1.2",
+        "exit-x": "^0.2.2",
+        "import-local": "^3.2.0",
+        "jest-config": "30.2.0",
+        "jest-util": "30.2.0",
+        "jest-validate": "30.2.0",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "jest": "bin/jest.js"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
+      },
+      "peerDependenciesMeta": {
+        "node-notifier": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-config": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-config/-/jest-config-30.2.0.tgz",
+      "integrity": "sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/core": "^7.27.4",
+        "@jest/get-type": "30.1.0",
+        "@jest/pattern": "30.0.1",
+        "@jest/test-sequencer": "30.2.0",
+        "@jest/types": "30.2.0",
+        "babel-jest": "30.2.0",
+        "chalk": "^4.1.2",
+        "ci-info": "^4.2.0",
+        "deepmerge": "^4.3.1",
+        "glob": "^10.3.10",
+        "graceful-fs": "^4.2.11",
+        "jest-circus": "30.2.0",
+        "jest-docblock": "30.2.0",
+        "jest-environment-node": "30.2.0",
+        "jest-regex-util": "30.0.1",
+        "jest-resolve": "30.2.0",
+        "jest-runner": "30.2.0",
+        "jest-util": "30.2.0",
+        "jest-validate": "30.2.0",
+        "micromatch": "^4.0.8",
+        "parse-json": "^5.2.0",
+        "pretty-format": "30.2.0",
+        "slash": "^3.0.0",
+        "strip-json-comments": "^3.1.1"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      },
+      "peerDependencies": {
+        "@types/node": "*",
+        "esbuild-register": ">=3.4.0",
+        "ts-node": ">=9.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "esbuild-register": {
+          "optional": true
+        },
+        "ts-node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-diff": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-diff/-/jest-diff-30.2.0.tgz",
+      "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/diff-sequences": "30.0.1",
+        "@jest/get-type": "30.1.0",
+        "chalk": "^4.1.2",
+        "pretty-format": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-docblock": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-docblock/-/jest-docblock-30.2.0.tgz",
+      "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "detect-newline": "^3.1.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-each": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-each/-/jest-each-30.2.0.tgz",
+      "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/get-type": "30.1.0",
+        "@jest/types": "30.2.0",
+        "chalk": "^4.1.2",
+        "jest-util": "30.2.0",
+        "pretty-format": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-environment-node": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-environment-node/-/jest-environment-node-30.2.0.tgz",
+      "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/environment": "30.2.0",
+        "@jest/fake-timers": "30.2.0",
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "jest-mock": "30.2.0",
+        "jest-util": "30.2.0",
+        "jest-validate": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-haste-map": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-haste-map/-/jest-haste-map-30.2.0.tgz",
+      "integrity": "sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "anymatch": "^3.1.3",
+        "fb-watchman": "^2.0.2",
+        "graceful-fs": "^4.2.11",
+        "jest-regex-util": "30.0.1",
+        "jest-util": "30.2.0",
+        "jest-worker": "30.2.0",
+        "micromatch": "^4.0.8",
+        "walker": "^1.0.8"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "^2.3.3"
+      }
+    },
+    "node_modules/jest-leak-detector": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz",
+      "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/get-type": "30.1.0",
+        "pretty-format": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-matcher-utils": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz",
+      "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/get-type": "30.1.0",
+        "chalk": "^4.1.2",
+        "jest-diff": "30.2.0",
+        "pretty-format": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-message-util": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-message-util/-/jest-message-util-30.2.0.tgz",
+      "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.27.1",
+        "@jest/types": "30.2.0",
+        "@types/stack-utils": "^2.0.3",
+        "chalk": "^4.1.2",
+        "graceful-fs": "^4.2.11",
+        "micromatch": "^4.0.8",
+        "pretty-format": "30.2.0",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.6"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-mock": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-mock/-/jest-mock-30.2.0.tgz",
+      "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "jest-util": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-pnp-resolver": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmmirror.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
+      "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      },
+      "peerDependencies": {
+        "jest-resolve": "*"
+      },
+      "peerDependenciesMeta": {
+        "jest-resolve": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/jest-regex-util": {
+      "version": "30.0.1",
+      "resolved": "https://registry.npmmirror.com/jest-regex-util/-/jest-regex-util-30.0.1.tgz",
+      "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-resolve": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-resolve/-/jest-resolve-30.2.0.tgz",
+      "integrity": "sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.2",
+        "graceful-fs": "^4.2.11",
+        "jest-haste-map": "30.2.0",
+        "jest-pnp-resolver": "^1.2.3",
+        "jest-util": "30.2.0",
+        "jest-validate": "30.2.0",
+        "slash": "^3.0.0",
+        "unrs-resolver": "^1.7.11"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-resolve-dependencies": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz",
+      "integrity": "sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "jest-regex-util": "30.0.1",
+        "jest-snapshot": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-runner": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-runner/-/jest-runner-30.2.0.tgz",
+      "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/console": "30.2.0",
+        "@jest/environment": "30.2.0",
+        "@jest/test-result": "30.2.0",
+        "@jest/transform": "30.2.0",
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "chalk": "^4.1.2",
+        "emittery": "^0.13.1",
+        "exit-x": "^0.2.2",
+        "graceful-fs": "^4.2.11",
+        "jest-docblock": "30.2.0",
+        "jest-environment-node": "30.2.0",
+        "jest-haste-map": "30.2.0",
+        "jest-leak-detector": "30.2.0",
+        "jest-message-util": "30.2.0",
+        "jest-resolve": "30.2.0",
+        "jest-runtime": "30.2.0",
+        "jest-util": "30.2.0",
+        "jest-watcher": "30.2.0",
+        "jest-worker": "30.2.0",
+        "p-limit": "^3.1.0",
+        "source-map-support": "0.5.13"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-runner/node_modules/source-map-support": {
+      "version": "0.5.13",
+      "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.13.tgz",
+      "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/jest-runtime": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-runtime/-/jest-runtime-30.2.0.tgz",
+      "integrity": "sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/environment": "30.2.0",
+        "@jest/fake-timers": "30.2.0",
+        "@jest/globals": "30.2.0",
+        "@jest/source-map": "30.0.1",
+        "@jest/test-result": "30.2.0",
+        "@jest/transform": "30.2.0",
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "chalk": "^4.1.2",
+        "cjs-module-lexer": "^2.1.0",
+        "collect-v8-coverage": "^1.0.2",
+        "glob": "^10.3.10",
+        "graceful-fs": "^4.2.11",
+        "jest-haste-map": "30.2.0",
+        "jest-message-util": "30.2.0",
+        "jest-mock": "30.2.0",
+        "jest-regex-util": "30.0.1",
+        "jest-resolve": "30.2.0",
+        "jest-snapshot": "30.2.0",
+        "jest-util": "30.2.0",
+        "slash": "^3.0.0",
+        "strip-bom": "^4.0.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-runtime/node_modules/strip-bom": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/strip-bom/-/strip-bom-4.0.0.tgz",
+      "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jest-snapshot": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-snapshot/-/jest-snapshot-30.2.0.tgz",
+      "integrity": "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/core": "^7.27.4",
+        "@babel/generator": "^7.27.5",
+        "@babel/plugin-syntax-jsx": "^7.27.1",
+        "@babel/plugin-syntax-typescript": "^7.27.1",
+        "@babel/types": "^7.27.3",
+        "@jest/expect-utils": "30.2.0",
+        "@jest/get-type": "30.1.0",
+        "@jest/snapshot-utils": "30.2.0",
+        "@jest/transform": "30.2.0",
+        "@jest/types": "30.2.0",
+        "babel-preset-current-node-syntax": "^1.2.0",
+        "chalk": "^4.1.2",
+        "expect": "30.2.0",
+        "graceful-fs": "^4.2.11",
+        "jest-diff": "30.2.0",
+        "jest-matcher-utils": "30.2.0",
+        "jest-message-util": "30.2.0",
+        "jest-util": "30.2.0",
+        "pretty-format": "30.2.0",
+        "semver": "^7.7.2",
+        "synckit": "^0.11.8"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-util": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-util/-/jest-util-30.2.0.tgz",
+      "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "chalk": "^4.1.2",
+        "ci-info": "^4.2.0",
+        "graceful-fs": "^4.2.11",
+        "picomatch": "^4.0.2"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-validate": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-validate/-/jest-validate-30.2.0.tgz",
+      "integrity": "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/get-type": "30.1.0",
+        "@jest/types": "30.2.0",
+        "camelcase": "^6.3.0",
+        "chalk": "^4.1.2",
+        "leven": "^3.1.0",
+        "pretty-format": "30.2.0"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-watcher": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-watcher/-/jest-watcher-30.2.0.tgz",
+      "integrity": "sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/test-result": "30.2.0",
+        "@jest/types": "30.2.0",
+        "@types/node": "*",
+        "ansi-escapes": "^4.3.2",
+        "chalk": "^4.1.2",
+        "emittery": "^0.13.1",
+        "jest-util": "30.2.0",
+        "string-length": "^4.0.2"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jest-worker": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-30.2.0.tgz",
+      "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "@ungap/structured-clone": "^1.3.0",
+        "jest-util": "30.2.0",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.1.1"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/jiti": {
+      "version": "1.21.7",
+      "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
+      "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "jiti": "bin/jiti.js"
+      }
+    },
+    "node_modules/js-tiktoken": {
+      "version": "1.0.21",
+      "resolved": "https://registry.npmmirror.com/js-tiktoken/-/js-tiktoken-1.0.21.tgz",
+      "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==",
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.5.1"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
+      "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/json-bigint": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz",
+      "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "bignumber.js": "^9.0.0"
+      }
+    },
+    "node_modules/json-bignum": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmmirror.com/json-bignum/-/json-bignum-0.0.3.tgz",
+      "integrity": "sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/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.npmmirror.com/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jsonc-parser": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+      "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/jsonfile": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.0.tgz",
+      "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "universalify": "^2.0.0"
+      },
+      "optionalDependencies": {
+        "graceful-fs": "^4.1.6"
+      }
+    },
+    "node_modules/jsonwebtoken": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
+      "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
+      "license": "MIT",
+      "dependencies": {
+        "jws": "^4.0.1",
+        "lodash.includes": "^4.3.0",
+        "lodash.isboolean": "^3.0.3",
+        "lodash.isinteger": "^4.0.4",
+        "lodash.isnumber": "^3.0.3",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.isstring": "^4.0.1",
+        "lodash.once": "^4.0.0",
+        "ms": "^2.1.1",
+        "semver": "^7.5.4"
+      },
+      "engines": {
+        "node": ">=12",
+        "npm": ">=6"
+      }
+    },
+    "node_modules/jwa": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/jwa/-/jwa-2.0.1.tgz",
+      "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+      "license": "MIT",
+      "dependencies": {
+        "buffer-equal-constant-time": "^1.0.1",
+        "ecdsa-sig-formatter": "1.0.11",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/jws": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/jws/-/jws-4.0.1.tgz",
+      "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+      "license": "MIT",
+      "dependencies": {
+        "jwa": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/katex": {
+      "version": "0.16.27",
+      "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.27.tgz",
+      "integrity": "sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==",
+      "funding": [
+        "https://opencollective.com/katex",
+        "https://github.com/sponsors/katex"
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "commander": "^8.3.0"
+      },
+      "bin": {
+        "katex": "cli.js"
+      }
+    },
+    "node_modules/katex/node_modules/commander": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+      "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmmirror.com/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/khroma": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
+      "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
+    },
+    "node_modules/langchain": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmmirror.com/langchain/-/langchain-1.1.5.tgz",
+      "integrity": "sha512-tmJHdCsi4AQLEWDeTm9QTWgdwYgIaA4kfp14KFw6e1sUPxjsoHqdFqdf1ZJZxhs1h/n+hpIr3NBfGNBQnWxWEQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@langchain/langgraph": "^1.0.0",
+        "@langchain/langgraph-checkpoint": "^1.0.0",
+        "langsmith": "~0.3.74",
+        "uuid": "^10.0.0",
+        "zod": "^3.25.76 || ^4"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "peerDependencies": {
+        "@langchain/core": "1.1.4"
+      }
+    },
+    "node_modules/langium": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz",
+      "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==",
+      "license": "MIT",
+      "dependencies": {
+        "chevrotain": "~11.0.3",
+        "chevrotain-allstar": "~0.3.0",
+        "vscode-languageserver": "~9.0.1",
+        "vscode-languageserver-textdocument": "~1.0.11",
+        "vscode-uri": "~3.0.8"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
+    "node_modules/langsmith": {
+      "version": "0.3.85",
+      "resolved": "https://registry.npmmirror.com/langsmith/-/langsmith-0.3.85.tgz",
+      "integrity": "sha512-Txuaxnpcra57qld4+hkqHhd9L2D6G6kEAReWXdr/sddxPu6ycBHXStqDciituC642lJmzPdarrYtll2vSwMbnQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/uuid": "^10.0.0",
+        "chalk": "^4.1.2",
+        "console-table-printer": "^2.12.1",
+        "p-queue": "^6.6.2",
+        "semver": "^7.6.3",
+        "uuid": "^10.0.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "*",
+        "@opentelemetry/exporter-trace-otlp-proto": "*",
+        "@opentelemetry/sdk-trace-base": "*",
+        "openai": "*"
+      },
+      "peerDependenciesMeta": {
+        "@opentelemetry/api": {
+          "optional": true
+        },
+        "@opentelemetry/exporter-trace-otlp-proto": {
+          "optional": true
+        },
+        "@opentelemetry/sdk-trace-base": {
+          "optional": true
+        },
+        "openai": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/layout-base": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
+      "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==",
+      "license": "MIT"
+    },
+    "node_modules/leven": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/leven/-/leven-3.1.0.tgz",
+      "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmmirror.com/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/libphonenumber-js": {
+      "version": "1.12.31",
+      "resolved": "https://registry.npmmirror.com/libphonenumber-js/-/libphonenumber-js-1.12.31.tgz",
+      "integrity": "sha512-Z3IhgVgrqO1S5xPYM3K5XwbkDasU67/Vys4heW+lfSBALcUZjeIIzI8zCLifY+OCzSq+fpDdywMDa7z+4srJPQ==",
+      "license": "MIT"
+    },
+    "node_modules/lilconfig": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+      "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/antonk52"
+      }
+    },
+    "node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/load-esm": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/load-esm/-/load-esm-1.0.3.tgz",
+      "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/Borewit"
+        },
+        {
+          "type": "buymeacoffee",
+          "url": "https://buymeacoffee.com/borewit"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=13.2.0"
+      }
+    },
+    "node_modules/loader-runner": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.1.tgz",
+      "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.11.5"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/locate-path": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
+      "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-locate": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "license": "MIT"
+    },
+    "node_modules/lodash-es": {
+      "version": "4.17.22",
+      "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.22.tgz",
+      "integrity": "sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.includes": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz",
+      "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isboolean": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+      "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isinteger": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmmirror.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+      "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isnumber": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+      "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isplainobject": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.isstring": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+      "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.memoize": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+      "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/lodash.once": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/lodash.once/-/lodash.once-4.1.1.tgz",
+      "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+      "license": "MIT"
+    },
+    "node_modules/log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/longest-streak": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/longest-streak/-/longest-streak-3.1.0.tgz",
+      "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/lowlight": {
+      "version": "1.20.0",
+      "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz",
+      "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==",
+      "license": "MIT",
+      "dependencies": {
+        "fault": "^1.0.0",
+        "highlight.js": "~10.7.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/lru-cache": {
+      "version": "10.4.3",
+      "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz",
+      "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+      "license": "ISC"
+    },
+    "node_modules/lucide-react": {
+      "version": "0.556.0",
+      "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.556.0.tgz",
+      "integrity": "sha512-iOb8dRk7kLaYBZhR2VlV1CeJGxChBgUthpSP8wom9jfj79qovgG6qcSdiy6vkoREKPnbUYzJsCn4o4PtG3Iy+A==",
+      "license": "ISC",
+      "peerDependencies": {
+        "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+      }
+    },
+    "node_modules/luxon": {
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz",
+      "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.17",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz",
+      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      }
+    },
+    "node_modules/make-dir": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-4.0.0.tgz",
+      "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.5.3"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmmirror.com/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "devOptional": true,
+      "license": "ISC"
+    },
+    "node_modules/makeerror": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmmirror.com/makeerror/-/makeerror-1.0.12.tgz",
+      "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "tmpl": "1.0.5"
+      }
+    },
+    "node_modules/markdown-table": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+      "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/marked": {
+      "version": "16.4.2",
+      "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",
+      "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",
+      "license": "MIT",
+      "bin": {
+        "marked": "bin/marked.js"
+      },
+      "engines": {
+        "node": ">= 20"
+      }
+    },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/mdast-util-find-and-replace": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+      "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "escape-string-regexp": "^5.0.0",
+        "unist-util-is": "^6.0.0",
+        "unist-util-visit-parents": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+      "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/mdast-util-from-markdown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
+      "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "@types/unist": "^3.0.0",
+        "decode-named-character-reference": "^1.0.0",
+        "devlop": "^1.0.0",
+        "mdast-util-to-string": "^4.0.0",
+        "micromark": "^4.0.0",
+        "micromark-util-decode-numeric-character-reference": "^2.0.0",
+        "micromark-util-decode-string": "^2.0.0",
+        "micromark-util-normalize-identifier": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0",
+        "unist-util-stringify-position": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+      "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+      "license": "MIT",
+      "dependencies": {
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-gfm-autolink-literal": "^2.0.0",
+        "mdast-util-gfm-footnote": "^2.0.0",
+        "mdast-util-gfm-strikethrough": "^2.0.0",
+        "mdast-util-gfm-table": "^2.0.0",
+        "mdast-util-gfm-task-list-item": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm-autolink-literal": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+      "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "ccount": "^2.0.0",
+        "devlop": "^1.0.0",
+        "mdast-util-find-and-replace": "^3.0.0",
+        "micromark-util-character": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm-footnote": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+      "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.1.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0",
+        "micromark-util-normalize-identifier": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm-strikethrough": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+      "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm-table": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+      "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.0.0",
+        "markdown-table": "^3.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-gfm-task-list-item": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+      "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-math": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz",
+      "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.0.0",
+        "longest-streak": "^3.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.1.0",
+        "unist-util-remove-position": "^5.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-mdx-expression": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+      "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree-jsx": "^1.0.0",
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-mdx-jsx": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
+      "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree-jsx": "^1.0.0",
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "@types/unist": "^3.0.0",
+        "ccount": "^2.0.0",
+        "devlop": "^1.1.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0",
+        "parse-entities": "^4.0.0",
+        "stringify-entities": "^4.0.0",
+        "unist-util-stringify-position": "^4.0.0",
+        "vfile-message": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-mdxjs-esm": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+      "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree-jsx": "^1.0.0",
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "mdast-util-to-markdown": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-phrasing": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+      "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "unist-util-is": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-to-hast": {
+      "version": "13.2.1",
+      "resolved": "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+      "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "@ungap/structured-clone": "^1.0.0",
+        "devlop": "^1.0.0",
+        "micromark-util-sanitize-uri": "^2.0.0",
+        "trim-lines": "^3.0.0",
+        "unist-util-position": "^5.0.0",
+        "unist-util-visit": "^5.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-to-markdown": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+      "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "@types/unist": "^3.0.0",
+        "longest-streak": "^3.0.0",
+        "mdast-util-phrasing": "^4.0.0",
+        "mdast-util-to-string": "^4.0.0",
+        "micromark-util-classify-character": "^2.0.0",
+        "micromark-util-decode-string": "^2.0.0",
+        "unist-util-visit": "^5.0.0",
+        "zwitch": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/mdast-util-to-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+      "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/media-typer": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-1.1.0.tgz",
+      "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/memfs": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmmirror.com/memfs/-/memfs-3.6.0.tgz",
+      "integrity": "sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ==",
+      "dev": true,
+      "license": "Unlicense",
+      "dependencies": {
+        "fs-monkey": "^1.0.4"
+      },
+      "engines": {
+        "node": ">= 4.0.0"
+      }
+    },
+    "node_modules/merge-descriptors": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+      "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/merge-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
+      "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/mermaid": {
+      "version": "11.12.2",
+      "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.12.2.tgz",
+      "integrity": "sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==",
+      "license": "MIT",
+      "dependencies": {
+        "@braintree/sanitize-url": "^7.1.1",
+        "@iconify/utils": "^3.0.1",
+        "@mermaid-js/parser": "^0.6.3",
+        "@types/d3": "^7.4.3",
+        "cytoscape": "^3.29.3",
+        "cytoscape-cose-bilkent": "^4.1.0",
+        "cytoscape-fcose": "^2.2.0",
+        "d3": "^7.9.0",
+        "d3-sankey": "^0.12.3",
+        "dagre-d3-es": "7.0.13",
+        "dayjs": "^1.11.18",
+        "dompurify": "^3.2.5",
+        "katex": "^0.16.22",
+        "khroma": "^2.1.0",
+        "lodash-es": "^4.17.21",
+        "marked": "^16.2.1",
+        "roughjs": "^4.6.6",
+        "stylis": "^4.3.6",
+        "ts-dedent": "^2.2.0",
+        "uuid": "^11.1.0"
+      }
+    },
+    "node_modules/mermaid/node_modules/uuid": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+      "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/esm/bin/uuid"
+      }
+    },
+    "node_modules/methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/micromark": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/micromark/-/micromark-4.0.2.tgz",
+      "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@types/debug": "^4.0.0",
+        "debug": "^4.0.0",
+        "decode-named-character-reference": "^1.0.0",
+        "devlop": "^1.0.0",
+        "micromark-core-commonmark": "^2.0.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-chunked": "^2.0.0",
+        "micromark-util-combine-extensions": "^2.0.0",
+        "micromark-util-decode-numeric-character-reference": "^2.0.0",
+        "micromark-util-encode": "^2.0.0",
+        "micromark-util-normalize-identifier": "^2.0.0",
+        "micromark-util-resolve-all": "^2.0.0",
+        "micromark-util-sanitize-uri": "^2.0.0",
+        "micromark-util-subtokenize": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-core-commonmark": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+      "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "decode-named-character-reference": "^1.0.0",
+        "devlop": "^1.0.0",
+        "micromark-factory-destination": "^2.0.0",
+        "micromark-factory-label": "^2.0.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-factory-title": "^2.0.0",
+        "micromark-factory-whitespace": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-chunked": "^2.0.0",
+        "micromark-util-classify-character": "^2.0.0",
+        "micromark-util-html-tag-name": "^2.0.0",
+        "micromark-util-normalize-identifier": "^2.0.0",
+        "micromark-util-resolve-all": "^2.0.0",
+        "micromark-util-subtokenize": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-extension-gfm": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+      "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+      "license": "MIT",
+      "dependencies": {
+        "micromark-extension-gfm-autolink-literal": "^2.0.0",
+        "micromark-extension-gfm-footnote": "^2.0.0",
+        "micromark-extension-gfm-strikethrough": "^2.0.0",
+        "micromark-extension-gfm-table": "^2.0.0",
+        "micromark-extension-gfm-tagfilter": "^2.0.0",
+        "micromark-extension-gfm-task-list-item": "^2.0.0",
+        "micromark-util-combine-extensions": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-autolink-literal": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+      "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-sanitize-uri": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-footnote": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+      "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-core-commonmark": "^2.0.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-normalize-identifier": "^2.0.0",
+        "micromark-util-sanitize-uri": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-strikethrough": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+      "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-util-chunked": "^2.0.0",
+        "micromark-util-classify-character": "^2.0.0",
+        "micromark-util-resolve-all": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-table": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+      "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-tagfilter": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+      "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-gfm-task-list-item": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+      "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-extension-math": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz",
+      "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/katex": "^0.16.0",
+        "devlop": "^1.0.0",
+        "katex": "^0.16.0",
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/micromark-factory-destination": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+      "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-factory-label": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+      "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-factory-space": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+      "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-factory-title": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+      "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-factory-whitespace": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+      "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-factory-space": "^2.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-character": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+      "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-chunked": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+      "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-symbol": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-classify-character": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+      "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-combine-extensions": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+      "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-chunked": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-decode-numeric-character-reference": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+      "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-symbol": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-decode-string": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+      "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "decode-named-character-reference": "^1.0.0",
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-decode-numeric-character-reference": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-encode": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+      "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/micromark-util-html-tag-name": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+      "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/micromark-util-normalize-identifier": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+      "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-symbol": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-resolve-all": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+      "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-sanitize-uri": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+      "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "micromark-util-character": "^2.0.0",
+        "micromark-util-encode": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-subtokenize": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+      "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "devlop": "^1.0.0",
+        "micromark-util-chunked": "^2.0.0",
+        "micromark-util-symbol": "^2.0.0",
+        "micromark-util-types": "^2.0.0"
+      }
+    },
+    "node_modules/micromark-util-symbol": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+      "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/micromark-util-types": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+      "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+      "funding": [
+        {
+          "type": "GitHub Sponsors",
+          "url": "https://github.com/sponsors/unifiedjs"
+        },
+        {
+          "type": "OpenCollective",
+          "url": "https://opencollective.com/unified"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz",
+      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "braces": "^3.0.3",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/micromatch/node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/mime": {
+      "version": "2.6.0",
+      "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz",
+      "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.54.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.54.0.tgz",
+      "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-3.0.2.tgz",
+      "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "^1.54.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/mimic-fn": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz",
+      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/mimic-response": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz",
+      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/minipass": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz",
+      "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/mkdirp": {
+      "version": "0.5.6",
+      "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz",
+      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "^1.2.6"
+      },
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      }
+    },
+    "node_modules/mkdirp-classic": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz",
+      "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
+      "license": "MIT"
+    },
+    "node_modules/mlly": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
+      "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.15.0",
+        "pathe": "^2.0.3",
+        "pkg-types": "^1.3.1",
+        "ufo": "^1.6.1"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/multer": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/multer/-/multer-2.0.2.tgz",
+      "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==",
+      "license": "MIT",
+      "dependencies": {
+        "append-field": "^1.0.0",
+        "busboy": "^1.6.0",
+        "concat-stream": "^2.0.0",
+        "mkdirp": "^0.5.6",
+        "object-assign": "^4.1.1",
+        "type-is": "^1.6.18",
+        "xtend": "^4.0.2"
+      },
+      "engines": {
+        "node": ">= 10.16.0"
+      }
+    },
+    "node_modules/multer/node_modules/media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/multer/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/multer/node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/multer/node_modules/type-is": {
+      "version": "1.6.18",
+      "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz",
+      "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+      "license": "MIT",
+      "dependencies": {
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.24"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mustache": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/mustache/-/mustache-4.2.0.tgz",
+      "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
+      "license": "MIT",
+      "bin": {
+        "mustache": "bin/mustache"
+      }
+    },
+    "node_modules/mute-stream": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/mute-stream/-/mute-stream-2.0.0.tgz",
+      "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==",
+      "dev": true,
+      "license": "ISC",
+      "engines": {
+        "node": "^18.17.0 || >=20.5.0"
+      }
+    },
+    "node_modules/mz": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+      "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "any-promise": "^1.0.0",
+        "object-assign": "^4.0.1",
+        "thenify-all": "^1.0.0"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmmirror.com/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/napi-build-utils": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-2.0.0.tgz",
+      "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==",
+      "license": "MIT"
+    },
+    "node_modules/napi-postinstall": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmmirror.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
+      "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "napi-postinstall": "lib/cli.js"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/napi-postinstall"
+      }
+    },
+    "node_modules/natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/negotiator": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-1.0.0.tgz",
+      "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/neo-async": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz",
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/node-abi": {
+      "version": "3.85.0",
+      "resolved": "https://registry.npmmirror.com/node-abi/-/node-abi-3.85.0.tgz",
+      "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==",
+      "license": "MIT",
+      "dependencies": {
+        "semver": "^7.3.5"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/node-abort-controller": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
+      "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/node-addon-api": {
+      "version": "8.5.0",
+      "resolved": "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-8.5.0.tgz",
+      "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==",
+      "license": "MIT",
+      "engines": {
+        "node": "^18 || ^20 || >= 21"
+      }
+    },
+    "node_modules/node-domexception": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/node-domexception/-/node-domexception-1.0.0.tgz",
+      "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/jimmywarting"
+        },
+        {
+          "type": "github",
+          "url": "https://paypal.me/jimmywarting"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.5.0"
+      }
+    },
+    "node_modules/node-edge-tts": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/node-edge-tts/-/node-edge-tts-1.2.8.tgz",
+      "integrity": "sha512-Yj290EUQJeO/n/LVoaFF1cxULB+1sROLm6w84o8zkwK7cdYqsMhxmhpac/d88AdbrjHjR+2zxEijvhNTtluGxQ==",
+      "license": "MIT",
+      "dependencies": {
+        "https-proxy-agent": "^7.0.1",
+        "ws": "^8.13.0",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "node-edge-tts": "bin.js"
+      }
+    },
+    "node_modules/node-emoji": {
+      "version": "1.11.0",
+      "resolved": "https://registry.npmmirror.com/node-emoji/-/node-emoji-1.11.0.tgz",
+      "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "lodash": "^4.17.21"
+      }
+    },
+    "node_modules/node-fetch": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-3.3.2.tgz",
+      "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+      "license": "MIT",
+      "dependencies": {
+        "data-uri-to-buffer": "^4.0.0",
+        "fetch-blob": "^3.1.4",
+        "formdata-polyfill": "^4.0.10"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/node-fetch"
+      }
+    },
+    "node_modules/node-gyp-build": {
+      "version": "4.8.4",
+      "resolved": "https://registry.npmmirror.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
+      "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
+      "license": "MIT",
+      "bin": {
+        "node-gyp-build": "bin.js",
+        "node-gyp-build-optional": "optional.js",
+        "node-gyp-build-test": "build-test.js"
+      }
+    },
+    "node_modules/node-int64": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/node-int64/-/node-int64-0.4.0.tgz",
+      "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.27",
+      "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz",
+      "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/npm-run-path": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz",
+      "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "path-key": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-hash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+      "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/object-inspect": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
+      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/on-finished": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz",
+      "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+      "license": "MIT",
+      "dependencies": {
+        "ee-first": "1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmmirror.com/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/onetime": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz",
+      "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mimic-fn": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/openai": {
+      "version": "6.10.0",
+      "resolved": "https://registry.npmmirror.com/openai/-/openai-6.10.0.tgz",
+      "integrity": "sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A==",
+      "license": "Apache-2.0",
+      "bin": {
+        "openai": "bin/cli"
+      },
+      "peerDependencies": {
+        "ws": "^8.18.0",
+        "zod": "^3.25 || ^4.0"
+      },
+      "peerDependenciesMeta": {
+        "ws": {
+          "optional": true
+        },
+        "zod": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/opencollective-postinstall": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
+      "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
+      "license": "MIT",
+      "bin": {
+        "opencollective-postinstall": "index.js"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.9.4",
+      "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz",
+      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0",
+        "word-wrap": "^1.2.5"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/ora": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz",
+      "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bl": "^4.1.0",
+        "chalk": "^4.1.0",
+        "cli-cursor": "^3.1.0",
+        "cli-spinners": "^2.5.0",
+        "is-interactive": "^1.0.0",
+        "is-unicode-supported": "^0.1.0",
+        "log-symbols": "^4.1.0",
+        "strip-ansi": "^6.0.0",
+        "wcwidth": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-finally": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-1.0.0.tgz",
+      "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
+      "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-limit": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/p-locate/node_modules/p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-queue": {
+      "version": "6.6.2",
+      "resolved": "https://registry.npmmirror.com/p-queue/-/p-queue-6.6.2.tgz",
+      "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
+      "license": "MIT",
+      "dependencies": {
+        "eventemitter3": "^4.0.4",
+        "p-timeout": "^3.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-retry": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz",
+      "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/retry": "0.12.0",
+        "retry": "^0.13.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/p-timeout": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/p-timeout/-/p-timeout-3.2.0.tgz",
+      "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
+      "license": "MIT",
+      "dependencies": {
+        "p-finally": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/package-json-from-dist": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+      "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+      "license": "BlueOak-1.0.0"
+    },
+    "node_modules/package-manager-detector": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz",
+      "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==",
+      "license": "MIT"
+    },
+    "node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+      "license": "(MIT AND Zlib)"
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-entities": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/parse-entities/-/parse-entities-4.0.2.tgz",
+      "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^2.0.0",
+        "character-entities-legacy": "^3.0.0",
+        "character-reference-invalid": "^2.0.0",
+        "decode-named-character-reference": "^1.0.0",
+        "is-alphanumerical": "^2.0.0",
+        "is-decimal": "^2.0.0",
+        "is-hexadecimal": "^2.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/parse-entities/node_modules/@types/unist": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-2.0.11.tgz",
+      "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+      "license": "MIT"
+    },
+    "node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/parse5": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+      "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+      "license": "MIT",
+      "dependencies": {
+        "entities": "^6.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parseurl": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz",
+      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/passport": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmmirror.com/passport/-/passport-0.7.0.tgz",
+      "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "passport-strategy": "1.x.x",
+        "pause": "0.0.1",
+        "utils-merge": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/jaredhanson"
+      }
+    },
+    "node_modules/passport-jwt": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/passport-jwt/-/passport-jwt-4.0.1.tgz",
+      "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==",
+      "license": "MIT",
+      "dependencies": {
+        "jsonwebtoken": "^9.0.0",
+        "passport-strategy": "^1.0.0"
+      }
+    },
+    "node_modules/passport-local": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/passport-local/-/passport-local-1.0.0.tgz",
+      "integrity": "sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==",
+      "dev": true,
+      "dependencies": {
+        "passport-strategy": "1.x.x"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/passport-strategy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/passport-strategy/-/passport-strategy-1.0.0.tgz",
+      "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/path-data-parser": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz",
+      "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==",
+      "license": "MIT"
+    },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/path-scurry": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz",
+      "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+      "license": "BlueOak-1.0.0",
+      "dependencies": {
+        "lru-cache": "^10.2.0",
+        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/path-to-regexp": {
+      "version": "8.3.0",
+      "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
+      "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/express"
+      }
+    },
+    "node_modules/path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pathe": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+      "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+      "license": "MIT"
+    },
+    "node_modules/pause": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmmirror.com/pause/-/pause-0.0.1.tgz",
+      "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
+    },
+    "node_modules/pdf-lib": {
+      "version": "1.17.1",
+      "resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
+      "integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
+      "license": "MIT",
+      "dependencies": {
+        "@pdf-lib/standard-fonts": "^1.0.0",
+        "@pdf-lib/upng": "^1.0.1",
+        "pako": "^1.0.11",
+        "tslib": "^1.11.1"
+      }
+    },
+    "node_modules/pdf-lib/node_modules/tslib": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+      "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+      "license": "0BSD"
+    },
+    "node_modules/pdf2image": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmmirror.com/pdf2image/-/pdf2image-1.2.3.tgz",
+      "integrity": "sha512-jBSu3Vt2TZsI+fdGRDtkezduzyvuzayy2HPAYVf1vX6tHLAl/HBcXRo4/lD/NDMoa+XsMo5ZeUYF86Lkc+CtLQ==",
+      "license": "MIT"
+    },
+    "node_modules/pdfjs-dist": {
+      "version": "4.10.38",
+      "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.10.38.tgz",
+      "integrity": "sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=20"
+      },
+      "optionalDependencies": {
+        "@napi-rs/canvas": "^0.1.65"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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/pify": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+      "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/pirates": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz",
+      "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/pkg-dir": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz",
+      "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "find-up": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pkg-types": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+      "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
+      "license": "MIT",
+      "dependencies": {
+        "confbox": "^0.1.8",
+        "mlly": "^1.7.4",
+        "pathe": "^2.0.1"
+      }
+    },
+    "node_modules/pluralize": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmmirror.com/pluralize/-/pluralize-8.0.0.tgz",
+      "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/points-on-curve": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz",
+      "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==",
+      "license": "MIT"
+    },
+    "node_modules/points-on-path": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz",
+      "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==",
+      "license": "MIT",
+      "dependencies": {
+        "path-data-parser": "0.1.0",
+        "points-on-curve": "0.2.0"
+      }
+    },
+    "node_modules/possible-typed-array-names": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+      "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "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",
+      "peer": true,
+      "dependencies": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/postcss-import": {
+      "version": "15.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+      "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "postcss-value-parser": "^4.0.0",
+        "read-cache": "^1.0.0",
+        "resolve": "^1.1.7"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.0.0"
+      }
+    },
+    "node_modules/postcss-js": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
+      "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "camelcase-css": "^2.0.1"
+      },
+      "engines": {
+        "node": "^12 || ^14 || >= 16"
+      },
+      "peerDependencies": {
+        "postcss": "^8.4.21"
+      }
+    },
+    "node_modules/postcss-load-config": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
+      "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "lilconfig": "^3.0.0",
+        "yaml": "^2.3.4"
+      },
+      "engines": {
+        "node": ">= 14"
+      },
+      "peerDependencies": {
+        "postcss": ">=8.0.9",
+        "ts-node": ">=9.0.0"
+      },
+      "peerDependenciesMeta": {
+        "postcss": {
+          "optional": true
+        },
+        "ts-node": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/postcss-nested": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+      "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "postcss-selector-parser": "^6.1.1"
+      },
+      "engines": {
+        "node": ">=12.0"
+      },
+      "peerDependencies": {
+        "postcss": "^8.2.14"
+      }
+    },
+    "node_modules/postcss-nested/node_modules/postcss-selector-parser": {
+      "version": "6.1.2",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+      "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postcss-selector-parser": {
+      "version": "6.0.10",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
+      "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/postcss-value-parser": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/prebuild-install": {
+      "version": "7.1.3",
+      "resolved": "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.3.tgz",
+      "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==",
+      "license": "MIT",
+      "dependencies": {
+        "detect-libc": "^2.0.0",
+        "expand-template": "^2.0.3",
+        "github-from-package": "0.0.0",
+        "minimist": "^1.2.3",
+        "mkdirp-classic": "^0.5.3",
+        "napi-build-utils": "^2.0.0",
+        "node-abi": "^3.3.0",
+        "pump": "^3.0.0",
+        "rc": "^1.2.7",
+        "simple-get": "^4.0.0",
+        "tar-fs": "^2.0.0",
+        "tunnel-agent": "^0.6.0"
+      },
+      "bin": {
+        "prebuild-install": "bin.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/prettier": {
+      "version": "3.7.4",
+      "resolved": "https://registry.npmmirror.com/prettier/-/prettier-3.7.4.tgz",
+      "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "bin": {
+        "prettier": "bin/prettier.cjs"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/prettier-linter-helpers": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "fast-diff": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/pretty-format": {
+      "version": "30.2.0",
+      "resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-30.2.0.tgz",
+      "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jest/schemas": "30.0.5",
+        "ansi-styles": "^5.2.0",
+        "react-is": "^18.3.1"
+      },
+      "engines": {
+        "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
+      }
+    },
+    "node_modules/pretty-format/node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/prismjs": {
+      "version": "1.30.0",
+      "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
+      "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/property-information": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmmirror.com/property-information/-/property-information-7.1.0.tgz",
+      "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/proxy-addr": {
+      "version": "2.0.7",
+      "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz",
+      "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+      "license": "MIT",
+      "dependencies": {
+        "forwarded": "0.2.0",
+        "ipaddr.js": "1.9.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
+    "node_modules/pump": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmmirror.com/pump/-/pump-3.0.3.tgz",
+      "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/pure-rand": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/pure-rand/-/pure-rand-7.0.1.tgz",
+      "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/dubzzz"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fast-check"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/qs": {
+      "version": "6.14.0",
+      "resolved": "https://registry.npmmirror.com/qs/-/qs-6.14.0.tgz",
+      "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "side-channel": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "node_modules/range-parser": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz",
+      "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/raw-body": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-3.0.2.tgz",
+      "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==",
+      "license": "MIT",
+      "dependencies": {
+        "bytes": "~3.1.2",
+        "http-errors": "~2.0.1",
+        "iconv-lite": "~0.7.0",
+        "unpipe": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/rc": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz",
+      "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==",
+      "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+      "dependencies": {
+        "deep-extend": "^0.6.0",
+        "ini": "~1.3.0",
+        "minimist": "^1.2.0",
+        "strip-json-comments": "~2.0.1"
+      },
+      "bin": {
+        "rc": "cli.js"
+      }
+    },
+    "node_modules/rc/node_modules/strip-json-comments": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react": {
+      "version": "19.2.1",
+      "resolved": "https://registry.npmmirror.com/react/-/react-19.2.1.tgz",
+      "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
+      "license": "MIT",
+      "peer": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react-dom": {
+      "version": "19.2.1",
+      "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.2.1.tgz",
+      "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
+      "license": "MIT",
+      "dependencies": {
+        "scheduler": "^0.27.0"
+      },
+      "peerDependencies": {
+        "react": "^19.2.1"
+      }
+    },
+    "node_modules/react-is": {
+      "version": "18.3.1",
+      "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz",
+      "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/react-markdown": {
+      "version": "10.1.0",
+      "resolved": "https://registry.npmmirror.com/react-markdown/-/react-markdown-10.1.0.tgz",
+      "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "devlop": "^1.0.0",
+        "hast-util-to-jsx-runtime": "^2.0.0",
+        "html-url-attributes": "^3.0.0",
+        "mdast-util-to-hast": "^13.0.0",
+        "remark-parse": "^11.0.0",
+        "remark-rehype": "^11.0.0",
+        "unified": "^11.0.0",
+        "unist-util-visit": "^5.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      },
+      "peerDependencies": {
+        "@types/react": ">=18",
+        "react": ">=18"
+      }
+    },
+    "node_modules/react-refresh": {
+      "version": "0.18.0",
+      "resolved": "https://registry.npmmirror.com/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-syntax-highlighter": {
+      "version": "16.1.0",
+      "resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-16.1.0.tgz",
+      "integrity": "sha512-E40/hBiP5rCNwkeBN1vRP+xow1X0pndinO+z3h7HLsHyjztbyjfzNWNKuAsJj+7DLam9iT4AaaOZnueCU+Nplg==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.28.4",
+        "highlight.js": "^10.4.1",
+        "highlightjs-vue": "^1.0.0",
+        "lowlight": "^1.17.0",
+        "prismjs": "^1.30.0",
+        "refractor": "^5.0.0"
+      },
+      "engines": {
+        "node": ">= 16.20.2"
+      },
+      "peerDependencies": {
+        "react": ">= 0.14.0"
+      }
+    },
+    "node_modules/read-cache": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+      "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "pify": "^2.3.0"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz",
+      "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.18.0"
+      },
+      "funding": {
+        "type": "individual",
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/reflect-metadata": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmmirror.com/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+      "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+      "license": "Apache-2.0",
+      "peer": true
+    },
+    "node_modules/refractor": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/refractor/-/refractor-5.0.0.tgz",
+      "integrity": "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/prismjs": "^1.0.0",
+        "hastscript": "^9.0.0",
+        "parse-entities": "^4.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+      "license": "MIT"
+    },
+    "node_modules/rehype-katex": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz",
+      "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/katex": "^0.16.0",
+        "hast-util-from-html-isomorphic": "^2.0.0",
+        "hast-util-to-text": "^4.0.0",
+        "katex": "^0.16.0",
+        "unist-util-visit-parents": "^6.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/remark-gfm": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+      "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "mdast-util-gfm": "^3.0.0",
+        "micromark-extension-gfm": "^3.0.0",
+        "remark-parse": "^11.0.0",
+        "remark-stringify": "^11.0.0",
+        "unified": "^11.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/remark-math": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz",
+      "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "mdast-util-math": "^3.0.0",
+        "micromark-extension-math": "^3.0.0",
+        "unified": "^11.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/remark-parse": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmmirror.com/remark-parse/-/remark-parse-11.0.0.tgz",
+      "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "mdast-util-from-markdown": "^2.0.0",
+        "micromark-util-types": "^2.0.0",
+        "unified": "^11.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/remark-rehype": {
+      "version": "11.1.2",
+      "resolved": "https://registry.npmmirror.com/remark-rehype/-/remark-rehype-11.1.2.tgz",
+      "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/hast": "^3.0.0",
+        "@types/mdast": "^4.0.0",
+        "mdast-util-to-hast": "^13.0.0",
+        "unified": "^11.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/remark-stringify": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+      "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/mdast": "^4.0.0",
+        "mdast-util-to-markdown": "^2.0.0",
+        "unified": "^11.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/resolve": {
+      "version": "1.22.11",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+      "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-core-module": "^2.16.1",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve-cwd": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+      "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "resolve-from": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/resolve-from": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz",
+      "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/restore-cursor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz",
+      "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "onetime": "^5.1.0",
+        "signal-exit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/restore-cursor/node_modules/signal-exit": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
+      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/retry": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz",
+      "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/reusify": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+      "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "iojs": ">=1.0.0",
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "5.0.10",
+      "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-5.0.10.tgz",
+      "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
+      "license": "ISC",
+      "dependencies": {
+        "glob": "^10.3.7"
+      },
+      "bin": {
+        "rimraf": "dist/esm/bin.mjs"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/robust-predicates": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
+      "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
+      "license": "Unlicense"
+    },
+    "node_modules/rollup": {
+      "version": "4.53.3",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.53.3.tgz",
+      "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
+      "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.53.3",
+        "@rollup/rollup-android-arm64": "4.53.3",
+        "@rollup/rollup-darwin-arm64": "4.53.3",
+        "@rollup/rollup-darwin-x64": "4.53.3",
+        "@rollup/rollup-freebsd-arm64": "4.53.3",
+        "@rollup/rollup-freebsd-x64": "4.53.3",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.53.3",
+        "@rollup/rollup-linux-arm-musleabihf": "4.53.3",
+        "@rollup/rollup-linux-arm64-gnu": "4.53.3",
+        "@rollup/rollup-linux-arm64-musl": "4.53.3",
+        "@rollup/rollup-linux-loong64-gnu": "4.53.3",
+        "@rollup/rollup-linux-ppc64-gnu": "4.53.3",
+        "@rollup/rollup-linux-riscv64-gnu": "4.53.3",
+        "@rollup/rollup-linux-riscv64-musl": "4.53.3",
+        "@rollup/rollup-linux-s390x-gnu": "4.53.3",
+        "@rollup/rollup-linux-x64-gnu": "4.53.3",
+        "@rollup/rollup-linux-x64-musl": "4.53.3",
+        "@rollup/rollup-openharmony-arm64": "4.53.3",
+        "@rollup/rollup-win32-arm64-msvc": "4.53.3",
+        "@rollup/rollup-win32-ia32-msvc": "4.53.3",
+        "@rollup/rollup-win32-x64-gnu": "4.53.3",
+        "@rollup/rollup-win32-x64-msvc": "4.53.3",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/roughjs": {
+      "version": "4.6.6",
+      "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz",
+      "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==",
+      "license": "MIT",
+      "dependencies": {
+        "hachure-fill": "^0.5.2",
+        "path-data-parser": "^0.1.0",
+        "points-on-curve": "^0.2.0",
+        "points-on-path": "^0.2.1"
+      }
+    },
+    "node_modules/router": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/router/-/router-2.2.0.tgz",
+      "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.4.0",
+        "depd": "^2.0.0",
+        "is-promise": "^4.0.0",
+        "parseurl": "^1.3.3",
+        "path-to-regexp": "^8.0.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "node_modules/rw": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+      "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/rxjs": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.2.tgz",
+      "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+      "license": "Apache-2.0",
+      "peer": true,
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "license": "MIT"
+    },
+    "node_modules/scheduler": {
+      "version": "0.27.0",
+      "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz",
+      "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+      "license": "MIT"
+    },
+    "node_modules/schema-utils": {
+      "version": "4.3.3",
+      "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.3.3.tgz",
+      "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/json-schema": "^7.0.9",
+        "ajv": "^8.9.0",
+        "ajv-formats": "^2.1.1",
+        "ajv-keywords": "^5.1.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      }
+    },
+    "node_modules/schema-utils/node_modules/ajv-formats": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz",
+      "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependencies": {
+        "ajv": "^8.0.0"
+      },
+      "peerDependenciesMeta": {
+        "ajv": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/secure-json-parse": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
+      "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ],
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/semver": {
+      "version": "7.7.3",
+      "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz",
+      "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+      "license": "ISC",
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/send": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/send/-/send-1.2.0.tgz",
+      "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+      "license": "MIT",
+      "dependencies": {
+        "debug": "^4.3.5",
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "etag": "^1.8.1",
+        "fresh": "^2.0.0",
+        "http-errors": "^2.0.0",
+        "mime-types": "^3.0.1",
+        "ms": "^2.1.3",
+        "on-finished": "^2.4.1",
+        "range-parser": "^1.2.1",
+        "statuses": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/serialize-javascript": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+      "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "node_modules/serve-static": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-2.2.0.tgz",
+      "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+      "license": "MIT",
+      "dependencies": {
+        "encodeurl": "^2.0.0",
+        "escape-html": "^1.0.3",
+        "parseurl": "^1.3.3",
+        "send": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 18"
+      }
+    },
+    "node_modules/server": {
+      "resolved": "server",
+      "link": true
+    },
+    "node_modules/set-function-length": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz",
+      "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+      "license": "MIT",
+      "dependencies": {
+        "define-data-property": "^1.1.4",
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2",
+        "get-intrinsic": "^1.2.4",
+        "gopd": "^1.0.1",
+        "has-property-descriptors": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "license": "ISC"
+    },
+    "node_modules/sha.js": {
+      "version": "2.4.12",
+      "resolved": "https://registry.npmmirror.com/sha.js/-/sha.js-2.4.12.tgz",
+      "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==",
+      "license": "(MIT AND BSD-3-Clause)",
+      "dependencies": {
+        "inherits": "^2.0.4",
+        "safe-buffer": "^5.2.1",
+        "to-buffer": "^1.2.0"
+      },
+      "bin": {
+        "sha.js": "bin.js"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "license": "MIT",
+      "dependencies": {
+        "shebang-regex": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/shell-quote": {
+      "version": "1.8.3",
+      "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.3.tgz",
+      "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.0.tgz",
+      "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3",
+        "side-channel-list": "^1.0.0",
+        "side-channel-map": "^1.0.1",
+        "side-channel-weakmap": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-list": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.0.tgz",
+      "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/side-channel-map/-/side-channel-map-1.0.1.tgz",
+      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/side-channel-weakmap": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.2",
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.5",
+        "object-inspect": "^1.13.3",
+        "side-channel-map": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/simple-concat": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz",
+      "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/simple-get": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz",
+      "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "decompress-response": "^6.0.0",
+        "once": "^1.3.1",
+        "simple-concat": "^1.0.0"
+      }
+    },
+    "node_modules/simple-kb": {
+      "resolved": "web",
+      "link": true
+    },
+    "node_modules/simple-wcswidth": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz",
+      "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==",
+      "license": "MIT"
+    },
+    "node_modules/slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/space-separated-tokens": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+      "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/spawn-command": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmmirror.com/spawn-command/-/spawn-command-0.0.2.tgz",
+      "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==",
+      "dev": true
+    },
+    "node_modules/sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/sql-highlight": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/sql-highlight/-/sql-highlight-6.1.0.tgz",
+      "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==",
+      "funding": [
+        "https://github.com/scriptcoded/sql-highlight?sponsor=1",
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/scriptcoded"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/stack-utils": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz",
+      "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "escape-string-regexp": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/stack-utils/node_modules/escape-string-regexp": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+      "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/statuses": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.2.tgz",
+      "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/streamsearch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz",
+      "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/string-length": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/string-length/-/string-length-4.0.2.tgz",
+      "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "char-regex": "^1.0.2",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/string-width-cjs": {
+      "name": "string-width",
+      "version": "4.2.3",
+      "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/stringify-entities": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmmirror.com/stringify-entities/-/stringify-entities-4.0.4.tgz",
+      "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+      "license": "MIT",
+      "dependencies": {
+        "character-entities-html4": "^2.0.0",
+        "character-entities-legacy": "^3.0.0"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi-cjs": {
+      "name": "strip-ansi",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/strip-final-newline": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
+      "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/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/strtok3": {
+      "version": "10.3.4",
+      "resolved": "https://registry.npmmirror.com/strtok3/-/strtok3-10.3.4.tgz",
+      "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==",
+      "license": "MIT",
+      "dependencies": {
+        "@tokenizer/token": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
+    "node_modules/style-to-js": {
+      "version": "1.1.21",
+      "resolved": "https://registry.npmmirror.com/style-to-js/-/style-to-js-1.1.21.tgz",
+      "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==",
+      "license": "MIT",
+      "dependencies": {
+        "style-to-object": "1.0.14"
+      }
+    },
+    "node_modules/style-to-object": {
+      "version": "1.0.14",
+      "resolved": "https://registry.npmmirror.com/style-to-object/-/style-to-object-1.0.14.tgz",
+      "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==",
+      "license": "MIT",
+      "dependencies": {
+        "inline-style-parser": "0.2.7"
+      }
+    },
+    "node_modules/stylis": {
+      "version": "4.3.6",
+      "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
+      "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
+      "license": "MIT"
+    },
+    "node_modules/sucrase": {
+      "version": "3.35.1",
+      "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
+      "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.2",
+        "commander": "^4.0.0",
+        "lines-and-columns": "^1.1.6",
+        "mz": "^2.7.0",
+        "pirates": "^4.0.1",
+        "tinyglobby": "^0.2.11",
+        "ts-interface-checker": "^0.1.9"
+      },
+      "bin": {
+        "sucrase": "bin/sucrase",
+        "sucrase-node": "bin/sucrase-node"
+      },
+      "engines": {
+        "node": ">=16 || 14 >=14.17"
+      }
+    },
+    "node_modules/superagent": {
+      "version": "10.2.3",
+      "resolved": "https://registry.npmmirror.com/superagent/-/superagent-10.2.3.tgz",
+      "integrity": "sha512-y/hkYGeXAj7wUMjxRbB21g/l6aAEituGXM9Rwl4o20+SX3e8YOSV6BxFXl+dL3Uk0mjSL3kCbNkwURm8/gEDig==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "component-emitter": "^1.3.1",
+        "cookiejar": "^2.1.4",
+        "debug": "^4.3.7",
+        "fast-safe-stringify": "^2.1.1",
+        "form-data": "^4.0.4",
+        "formidable": "^3.5.4",
+        "methods": "^1.1.2",
+        "mime": "2.6.0",
+        "qs": "^6.11.2"
+      },
+      "engines": {
+        "node": ">=14.18.0"
+      }
+    },
+    "node_modules/supertest": {
+      "version": "7.1.4",
+      "resolved": "https://registry.npmmirror.com/supertest/-/supertest-7.1.4.tgz",
+      "integrity": "sha512-tjLPs7dVyqgItVFirHYqe2T+MfWc2VOBQ8QFKKbWTA3PU7liZR8zoSpAi/C1k1ilm9RsXIKYf197oap9wXGVYg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "methods": "^1.1.2",
+        "superagent": "^10.2.3"
+      },
+      "engines": {
+        "node": ">=14.18.0"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/symbol-observable": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/symbol-observable/-/symbol-observable-4.0.0.tgz",
+      "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/synckit": {
+      "version": "0.11.11",
+      "resolved": "https://registry.npmmirror.com/synckit/-/synckit-0.11.11.tgz",
+      "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@pkgr/core": "^0.2.9"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/synckit"
+      }
+    },
+    "node_modules/table-layout": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/table-layout/-/table-layout-4.1.1.tgz",
+      "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==",
+      "license": "MIT",
+      "dependencies": {
+        "array-back": "^6.2.2",
+        "wordwrapjs": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=12.17"
+      }
+    },
+    "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",
+      "peer": true
+    },
+    "node_modules/tapable": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/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/tar-fs": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.4.tgz",
+      "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "chownr": "^1.1.1",
+        "mkdirp-classic": "^0.5.2",
+        "pump": "^3.0.0",
+        "tar-stream": "^2.1.4"
+      }
+    },
+    "node_modules/tar-stream": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz",
+      "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+      "license": "MIT",
+      "dependencies": {
+        "bl": "^4.0.3",
+        "end-of-stream": "^1.4.1",
+        "fs-constants": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^3.1.1"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/terser": {
+      "version": "5.44.1",
+      "resolved": "https://registry.npmmirror.com/terser/-/terser-5.44.1.tgz",
+      "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "peer": true,
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.15.0",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser-webpack-plugin": {
+      "version": "5.3.15",
+      "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.15.tgz",
+      "integrity": "sha512-PGkOdpRFK+rb1TzVz+msVhw4YMRT9txLF4kRqvJhGhCM324xuR3REBSHALN+l+sAhKUmz0aotnjp5D+P83mLhQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "jest-worker": "^27.4.5",
+        "schema-utils": "^4.3.0",
+        "serialize-javascript": "^6.0.2",
+        "terser": "^5.31.1"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependencies": {
+        "webpack": "^5.1.0"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "esbuild": {
+          "optional": true
+        },
+        "uglify-js": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/terser-webpack-plugin/node_modules/jest-worker": {
+      "version": "27.5.1",
+      "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz",
+      "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*",
+        "merge-stream": "^2.0.0",
+        "supports-color": "^8.0.0"
+      },
+      "engines": {
+        "node": ">= 10.13.0"
+      }
+    },
+    "node_modules/terser/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/tesseract.js": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-7.0.0.tgz",
+      "integrity": "sha512-exPBkd+z+wM1BuMkx/Bjv43OeLBxhL5kKWsz/9JY+DXcXdiBjiAch0V49QR3oAJqCaL5qURE0vx9Eo+G5YE7mA==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bmp-js": "^0.1.0",
+        "idb-keyval": "^6.2.0",
+        "is-url": "^1.2.4",
+        "node-fetch": "^2.6.9",
+        "opencollective-postinstall": "^2.0.3",
+        "regenerator-runtime": "^0.13.3",
+        "tesseract.js-core": "^7.0.0",
+        "wasm-feature-detect": "^1.8.0",
+        "zlibjs": "^0.3.1"
+      }
+    },
+    "node_modules/tesseract.js-core": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-7.0.0.tgz",
+      "integrity": "sha512-WnNH518NzmbSq9zgTPeoF8c+xmilS8rFIl1YKbk/ptuuc7p6cLNELNuPAzcmsYw450ca6bLa8j3t0VAtq435Vw==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/tesseract.js/node_modules/node-fetch": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/test-exclude": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/test-exclude/-/test-exclude-6.0.0.tgz",
+      "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@istanbuljs/schema": "^0.1.2",
+        "glob": "^7.1.4",
+        "minimatch": "^3.0.4"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/test-exclude/node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/text-segmentation": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
+      "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
+      "license": "MIT",
+      "dependencies": {
+        "utrie": "^1.0.2"
+      }
+    },
+    "node_modules/thenify": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+      "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "any-promise": "^1.0.0"
+      }
+    },
+    "node_modules/thenify-all": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+      "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "thenify": ">= 3.1.0 < 4"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/tinyexec": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz",
+      "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/tinyglobby": {
+      "version": "0.2.15",
+      "resolved": "https://registry.npmmirror.com/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/tmpl": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/tmpl/-/tmpl-1.0.5.tgz",
+      "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
+      "dev": true,
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/to-buffer": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/to-buffer/-/to-buffer-1.2.2.tgz",
+      "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
+      "license": "MIT",
+      "dependencies": {
+        "isarray": "^2.0.5",
+        "safe-buffer": "^5.2.1",
+        "typed-array-buffer": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/token-types": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmmirror.com/token-types/-/token-types-6.1.1.tgz",
+      "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@borewit/text-codec": "^0.1.0",
+        "@tokenizer/token": "^0.3.0",
+        "ieee754": "^1.2.1"
+      },
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/Borewit"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+      "license": "MIT"
+    },
+    "node_modules/tree-kill": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz",
+      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "tree-kill": "cli.js"
+      }
+    },
+    "node_modules/trim-lines": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz",
+      "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/trough": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/trough/-/trough-2.2.0.tgz",
+      "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/ts-api-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+      "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18.12"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.8.4"
+      }
+    },
+    "node_modules/ts-dedent": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
+      "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.10"
+      }
+    },
+    "node_modules/ts-interface-checker": {
+      "version": "0.1.13",
+      "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+      "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+      "dev": true,
+      "license": "Apache-2.0"
+    },
+    "node_modules/ts-jest": {
+      "version": "29.4.6",
+      "resolved": "https://registry.npmmirror.com/ts-jest/-/ts-jest-29.4.6.tgz",
+      "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "bs-logger": "^0.2.6",
+        "fast-json-stable-stringify": "^2.1.0",
+        "handlebars": "^4.7.8",
+        "json5": "^2.2.3",
+        "lodash.memoize": "^4.1.2",
+        "make-error": "^1.3.6",
+        "semver": "^7.7.3",
+        "type-fest": "^4.41.0",
+        "yargs-parser": "^21.1.1"
+      },
+      "bin": {
+        "ts-jest": "cli.js"
+      },
+      "engines": {
+        "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0"
+      },
+      "peerDependencies": {
+        "@babel/core": ">=7.0.0-beta.0 <8",
+        "@jest/transform": "^29.0.0 || ^30.0.0",
+        "@jest/types": "^29.0.0 || ^30.0.0",
+        "babel-jest": "^29.0.0 || ^30.0.0",
+        "jest": "^29.0.0 || ^30.0.0",
+        "jest-util": "^29.0.0 || ^30.0.0",
+        "typescript": ">=4.3 <6"
+      },
+      "peerDependenciesMeta": {
+        "@babel/core": {
+          "optional": true
+        },
+        "@jest/transform": {
+          "optional": true
+        },
+        "@jest/types": {
+          "optional": true
+        },
+        "babel-jest": {
+          "optional": true
+        },
+        "esbuild": {
+          "optional": true
+        },
+        "jest-util": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/ts-loader": {
+      "version": "9.5.4",
+      "resolved": "https://registry.npmmirror.com/ts-loader/-/ts-loader-9.5.4.tgz",
+      "integrity": "sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "enhanced-resolve": "^5.0.0",
+        "micromatch": "^4.0.0",
+        "semver": "^7.3.4",
+        "source-map": "^0.7.4"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "typescript": "*",
+        "webpack": "^5.0.0"
+      }
+    },
+    "node_modules/ts-loader/node_modules/source-map": {
+      "version": "0.7.6",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.6.tgz",
+      "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">= 12"
+      }
+    },
+    "node_modules/tsconfig-paths": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
+      "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "json5": "^2.2.2",
+        "minimist": "^1.2.6",
+        "strip-bom": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/tsconfig-paths-webpack-plugin": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz",
+      "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "enhanced-resolve": "^5.7.0",
+        "tapable": "^2.2.1",
+        "tsconfig-paths": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "license": "0BSD"
+    },
+    "node_modules/tunnel-agent": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prelude-ls": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/type-detect": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmmirror.com/type-detect/-/type-detect-4.0.8.tgz",
+      "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/type-fest": {
+      "version": "4.41.0",
+      "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-4.41.0.tgz",
+      "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+      "dev": true,
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/type-is": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/type-is/-/type-is-2.0.1.tgz",
+      "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+      "license": "MIT",
+      "dependencies": {
+        "content-type": "^1.0.5",
+        "media-typer": "^1.1.0",
+        "mime-types": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/typed-array-buffer": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+      "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bound": "^1.0.3",
+        "es-errors": "^1.3.0",
+        "is-typed-array": "^1.1.14"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+      "license": "MIT"
+    },
+    "node_modules/typeorm": {
+      "version": "0.3.26",
+      "resolved": "https://registry.npmmirror.com/typeorm/-/typeorm-0.3.26.tgz",
+      "integrity": "sha512-o2RrBNn3lczx1qv4j+JliVMmtkPSqEGpG0UuZkt9tCfWkoXKu8MZnjvp2GjWPll1SehwemQw6xrbVRhmOglj8Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@sqltools/formatter": "^1.2.5",
+        "ansis": "^3.17.0",
+        "app-root-path": "^3.1.0",
+        "buffer": "^6.0.3",
+        "dayjs": "^1.11.13",
+        "debug": "^4.4.0",
+        "dedent": "^1.6.0",
+        "dotenv": "^16.4.7",
+        "glob": "^10.4.5",
+        "sha.js": "^2.4.11",
+        "sql-highlight": "^6.0.0",
+        "tslib": "^2.8.1",
+        "uuid": "^11.1.0",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "typeorm": "cli.js",
+        "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js",
+        "typeorm-ts-node-esm": "cli-ts-node-esm.js"
+      },
+      "engines": {
+        "node": ">=16.13.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/typeorm"
+      },
+      "peerDependencies": {
+        "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0",
+        "@sap/hana-client": "^2.14.22",
+        "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0",
+        "ioredis": "^5.0.4",
+        "mongodb": "^5.8.0 || ^6.0.0",
+        "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1",
+        "mysql2": "^2.2.5 || ^3.0.1",
+        "oracledb": "^6.3.0",
+        "pg": "^8.5.1",
+        "pg-native": "^3.0.0",
+        "pg-query-stream": "^4.0.0",
+        "redis": "^3.1.1 || ^4.0.0 || ^5.0.14",
+        "reflect-metadata": "^0.1.14 || ^0.2.0",
+        "sql.js": "^1.4.0",
+        "sqlite3": "^5.0.3",
+        "ts-node": "^10.7.0",
+        "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@google-cloud/spanner": {
+          "optional": true
+        },
+        "@sap/hana-client": {
+          "optional": true
+        },
+        "better-sqlite3": {
+          "optional": true
+        },
+        "ioredis": {
+          "optional": true
+        },
+        "mongodb": {
+          "optional": true
+        },
+        "mssql": {
+          "optional": true
+        },
+        "mysql2": {
+          "optional": true
+        },
+        "oracledb": {
+          "optional": true
+        },
+        "pg": {
+          "optional": true
+        },
+        "pg-native": {
+          "optional": true
+        },
+        "pg-query-stream": {
+          "optional": true
+        },
+        "redis": {
+          "optional": true
+        },
+        "sql.js": {
+          "optional": true
+        },
+        "sqlite3": {
+          "optional": true
+        },
+        "ts-node": {
+          "optional": true
+        },
+        "typeorm-aurora-data-api-driver": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/typeorm/node_modules/ansis": {
+      "version": "3.17.0",
+      "resolved": "https://registry.npmmirror.com/ansis/-/ansis-3.17.0.tgz",
+      "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/typeorm/node_modules/uuid": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz",
+      "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/esm/bin/uuid"
+      }
+    },
+    "node_modules/typescript": {
+      "version": "5.9.3",
+      "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz",
+      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+      "devOptional": true,
+      "license": "Apache-2.0",
+      "peer": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/typescript-eslint": {
+      "version": "8.49.0",
+      "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.49.0.tgz",
+      "integrity": "sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@typescript-eslint/eslint-plugin": "8.49.0",
+        "@typescript-eslint/parser": "8.49.0",
+        "@typescript-eslint/typescript-estree": "8.49.0",
+        "@typescript-eslint/utils": "8.49.0"
+      },
+      "engines": {
+        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/typescript-eslint"
+      },
+      "peerDependencies": {
+        "eslint": "^8.57.0 || ^9.0.0",
+        "typescript": ">=4.8.4 <6.0.0"
+      }
+    },
+    "node_modules/typical": {
+      "version": "7.3.0",
+      "resolved": "https://registry.npmmirror.com/typical/-/typical-7.3.0.tgz",
+      "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.17"
+      }
+    },
+    "node_modules/ufo": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
+      "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==",
+      "license": "MIT"
+    },
+    "node_modules/uglify-js": {
+      "version": "3.19.3",
+      "resolved": "https://registry.npmmirror.com/uglify-js/-/uglify-js-3.19.3.tgz",
+      "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "optional": true,
+      "bin": {
+        "uglifyjs": "bin/uglifyjs"
+      },
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/uid": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/uid/-/uid-2.0.2.tgz",
+      "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==",
+      "license": "MIT",
+      "dependencies": {
+        "@lukeed/csprng": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/uint8array-extras": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmmirror.com/uint8array-extras/-/uint8array-extras-1.5.0.tgz",
+      "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/undici": {
+      "version": "7.16.0",
+      "resolved": "https://registry.npmmirror.com/undici/-/undici-7.16.0.tgz",
+      "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=20.18.1"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "7.16.0",
+      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.16.0.tgz",
+      "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+      "license": "MIT"
+    },
+    "node_modules/unified": {
+      "version": "11.0.5",
+      "resolved": "https://registry.npmmirror.com/unified/-/unified-11.0.5.tgz",
+      "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "bail": "^2.0.0",
+        "devlop": "^1.0.0",
+        "extend": "^3.0.0",
+        "is-plain-obj": "^4.0.0",
+        "trough": "^2.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-find-after": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz",
+      "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "unist-util-is": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-is": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.1.tgz",
+      "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-position": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/unist-util-position/-/unist-util-position-5.0.0.tgz",
+      "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-remove-position": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz",
+      "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "unist-util-visit": "^5.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-stringify-position": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+      "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-visit": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
+      "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "unist-util-is": "^6.0.0",
+        "unist-util-visit-parents": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/unist-util-visit-parents": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmmirror.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz",
+      "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "unist-util-is": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/universalify": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+      "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/unrs-resolver": {
+      "version": "1.11.1",
+      "resolved": "https://registry.npmmirror.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz",
+      "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "napi-postinstall": "^0.3.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/unrs-resolver"
+      },
+      "optionalDependencies": {
+        "@unrs/resolver-binding-android-arm-eabi": "1.11.1",
+        "@unrs/resolver-binding-android-arm64": "1.11.1",
+        "@unrs/resolver-binding-darwin-arm64": "1.11.1",
+        "@unrs/resolver-binding-darwin-x64": "1.11.1",
+        "@unrs/resolver-binding-freebsd-x64": "1.11.1",
+        "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1",
+        "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1",
+        "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1",
+        "@unrs/resolver-binding-linux-arm64-musl": "1.11.1",
+        "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1",
+        "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1",
+        "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1",
+        "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1",
+        "@unrs/resolver-binding-linux-x64-gnu": "1.11.1",
+        "@unrs/resolver-binding-linux-x64-musl": "1.11.1",
+        "@unrs/resolver-binding-wasm32-wasi": "1.11.1",
+        "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1",
+        "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1",
+        "@unrs/resolver-binding-win32-x64-msvc": "1.11.1"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
+      "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==",
+      "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.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "license": "MIT"
+    },
+    "node_modules/utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/utrie": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
+      "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
+      "license": "MIT",
+      "dependencies": {
+        "base64-arraybuffer": "^1.0.2"
+      }
+    },
+    "node_modules/uuid": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmmirror.com/uuid/-/uuid-10.0.0.tgz",
+      "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/v8-compile-cache-lib": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+      "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "node_modules/v8-to-istanbul": {
+      "version": "9.3.0",
+      "resolved": "https://registry.npmmirror.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz",
+      "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "^0.3.12",
+        "@types/istanbul-lib-coverage": "^2.0.1",
+        "convert-source-map": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10.12.0"
+      }
+    },
+    "node_modules/validator": {
+      "version": "13.15.23",
+      "resolved": "https://registry.npmmirror.com/validator/-/validator-13.15.23.tgz",
+      "integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/vfile": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmmirror.com/vfile/-/vfile-6.0.3.tgz",
+      "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "vfile-message": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/vfile-location": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
+      "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "vfile": "^6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/vfile-message": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/vfile-message/-/vfile-message-4.0.3.tgz",
+      "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/unist": "^3.0.0",
+        "unist-util-stringify-position": "^4.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/unified"
+      }
+    },
+    "node_modules/vite": {
+      "version": "6.4.1",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-6.4.1.tgz",
+      "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "esbuild": "^0.25.0",
+        "fdir": "^6.4.4",
+        "picomatch": "^4.0.2",
+        "postcss": "^8.5.3",
+        "rollup": "^4.34.9",
+        "tinyglobby": "^0.2.13"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+        "jiti": ">=1.21.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.16.0",
+        "tsx": "^4.8.1",
+        "yaml": "^2.4.2"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "jiti": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        },
+        "tsx": {
+          "optional": true
+        },
+        "yaml": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vscode-jsonrpc": {
+      "version": "8.2.0",
+      "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
+      "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/vscode-languageserver": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz",
+      "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==",
+      "license": "MIT",
+      "dependencies": {
+        "vscode-languageserver-protocol": "3.17.5"
+      },
+      "bin": {
+        "installServerIntoExtension": "bin/installServerIntoExtension"
+      }
+    },
+    "node_modules/vscode-languageserver-protocol": {
+      "version": "3.17.5",
+      "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
+      "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
+      "license": "MIT",
+      "dependencies": {
+        "vscode-jsonrpc": "8.2.0",
+        "vscode-languageserver-types": "3.17.5"
+      }
+    },
+    "node_modules/vscode-languageserver-textdocument": {
+      "version": "1.0.12",
+      "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz",
+      "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==",
+      "license": "MIT"
+    },
+    "node_modules/vscode-languageserver-types": {
+      "version": "3.17.5",
+      "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
+      "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
+      "license": "MIT"
+    },
+    "node_modules/vscode-uri": {
+      "version": "3.0.8",
+      "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz",
+      "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==",
+      "license": "MIT"
+    },
+    "node_modules/walker": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/walker/-/walker-1.0.8.tgz",
+      "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "makeerror": "1.0.12"
+      }
+    },
+    "node_modules/wasm-feature-detect": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz",
+      "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/watchpack": {
+      "version": "2.4.4",
+      "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.4.tgz",
+      "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.1.2"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/wcwidth": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz",
+      "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "defaults": "^1.0.3"
+      }
+    },
+    "node_modules/web-namespaces": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+      "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "node_modules/web-streams-polyfill": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmmirror.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+      "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/webpack": {
+      "version": "5.103.0",
+      "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.103.0.tgz",
+      "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@types/eslint-scope": "^3.7.7",
+        "@types/estree": "^1.0.8",
+        "@types/json-schema": "^7.0.15",
+        "@webassemblyjs/ast": "^1.14.1",
+        "@webassemblyjs/wasm-edit": "^1.14.1",
+        "@webassemblyjs/wasm-parser": "^1.14.1",
+        "acorn": "^8.15.0",
+        "acorn-import-phases": "^1.0.3",
+        "browserslist": "^4.26.3",
+        "chrome-trace-event": "^1.0.2",
+        "enhanced-resolve": "^5.17.3",
+        "es-module-lexer": "^1.2.1",
+        "eslint-scope": "5.1.1",
+        "events": "^3.2.0",
+        "glob-to-regexp": "^0.4.1",
+        "graceful-fs": "^4.2.11",
+        "json-parse-even-better-errors": "^2.3.1",
+        "loader-runner": "^4.3.1",
+        "mime-types": "^2.1.27",
+        "neo-async": "^2.6.2",
+        "schema-utils": "^4.3.3",
+        "tapable": "^2.3.0",
+        "terser-webpack-plugin": "^5.3.11",
+        "watchpack": "^2.4.4",
+        "webpack-sources": "^3.3.3"
+      },
+      "bin": {
+        "webpack": "bin/webpack.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/webpack"
+      },
+      "peerDependenciesMeta": {
+        "webpack-cli": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/webpack-node-externals": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz",
+      "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/webpack-sources": {
+      "version": "3.3.3",
+      "resolved": "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.3.3.tgz",
+      "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/webpack/node_modules/eslint-scope": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz",
+      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "dependencies": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^4.1.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/webpack/node_modules/estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true,
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/webpack/node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/webpack/node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "license": "ISC",
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "node-which": "bin/node-which"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/which-typed-array": {
+      "version": "1.1.19",
+      "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.19.tgz",
+      "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+      "license": "MIT",
+      "dependencies": {
+        "available-typed-arrays": "^1.0.7",
+        "call-bind": "^1.0.8",
+        "call-bound": "^1.0.4",
+        "for-each": "^0.3.5",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-tostringtag": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz",
+      "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/wordwrap": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/wordwrap/-/wordwrap-1.0.0.tgz",
+      "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/wordwrapjs": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/wordwrapjs/-/wordwrapjs-5.1.1.tgz",
+      "integrity": "sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12.17"
+      }
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrap-ansi-cjs": {
+      "name": "wrap-ansi",
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "license": "ISC"
+    },
+    "node_modules/write-file-atomic": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz",
+      "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "imurmurhash": "^0.1.4",
+        "signal-exit": "^4.0.1"
+      },
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
+    "node_modules/ws": {
+      "version": "8.18.3",
+      "resolved": "https://registry.npmmirror.com/ws/-/ws-8.18.3.tgz",
+      "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/yaml": {
+      "version": "2.8.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
+      "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
+      "dev": true,
+      "license": "ISC",
+      "peer": true,
+      "bin": {
+        "yaml": "bin.mjs"
+      },
+      "engines": {
+        "node": ">= 14.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/eemeli"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmmirror.com/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "devOptional": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/yoctocolors-cjs": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmmirror.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
+      "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/zlibjs": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz",
+      "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/zod": {
+      "version": "4.1.13",
+      "resolved": "https://registry.npmmirror.com/zod/-/zod-4.1.13.tgz",
+      "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
+      "license": "MIT",
+      "peer": true,
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
+    },
+    "node_modules/zwitch": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz",
+      "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+      "license": "MIT",
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/wooorm"
+      }
+    },
+    "server": {
+      "version": "0.0.1",
+      "license": "UNLICENSED",
+      "dependencies": {
+        "@elastic/elasticsearch": "^9.2.0",
+        "@langchain/core": "^1.1.5",
+        "@langchain/openai": "^1.1.3",
+        "@langchain/textsplitters": "^1.0.1",
+        "@nestjs/common": "^11.0.1",
+        "@nestjs/config": "^4.0.2",
+        "@nestjs/core": "^11.0.1",
+        "@nestjs/jwt": "^11.0.2",
+        "@nestjs/mapped-types": "^2.1.0",
+        "@nestjs/passport": "^11.0.5",
+        "@nestjs/platform-express": "^11.0.1",
+        "@nestjs/schedule": "^6.1.0",
+        "@nestjs/serve-static": "^5.0.4",
+        "@nestjs/typeorm": "^11.0.0",
+        "@types/cron": "^2.0.1",
+        "axios": "^1.13.2",
+        "bcrypt": "^6.0.0",
+        "better-sqlite3": "^12.5.0",
+        "class-transformer": "^0.5.1",
+        "class-validator": "^0.14.3",
+        "dotenv": "^17.2.3",
+        "form-data": "^4.0.5",
+        "langchain": "^1.1.5",
+        "node-edge-tts": "^1.2.8",
+        "passport": "^0.7.0",
+        "passport-jwt": "^4.0.1",
+        "pdf-lib": "^1.17.1",
+        "pdf2image": "^1.2.3",
+        "reflect-metadata": "^0.2.2",
+        "rxjs": "^7.8.1",
+        "tesseract.js": "^7.0.0",
+        "typeorm": "0.3.26"
+      },
+      "devDependencies": {
+        "@eslint/eslintrc": "^3.2.0",
+        "@eslint/js": "^9.18.0",
+        "@nestjs/cli": "^11.0.0",
+        "@nestjs/schematics": "^11.0.0",
+        "@nestjs/testing": "^11.0.1",
+        "@types/bcrypt": "^6.0.0",
+        "@types/better-sqlite3": "^7.6.13",
+        "@types/express": "^5.0.0",
+        "@types/jest": "^30.0.0",
+        "@types/multer": "^2.0.0",
+        "@types/node": "^22.10.7",
+        "@types/passport-jwt": "^4.0.1",
+        "@types/passport-local": "^1.0.38",
+        "@types/supertest": "^6.0.2",
+        "eslint": "^9.18.0",
+        "eslint-config-prettier": "^10.0.1",
+        "eslint-plugin-prettier": "^5.2.2",
+        "globals": "^16.0.0",
+        "jest": "^30.0.0",
+        "passport-local": "^1.0.0",
+        "prettier": "^3.4.2",
+        "source-map-support": "^0.5.21",
+        "supertest": "^7.0.0",
+        "ts-jest": "^29.2.5",
+        "ts-loader": "^9.5.2",
+        "ts-node": "^10.9.2",
+        "tsconfig-paths": "^4.2.0",
+        "typescript": "^5.7.3",
+        "typescript-eslint": "^8.20.0"
+      }
+    },
+    "server/node_modules/@types/node": {
+      "version": "22.19.2",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.19.2.tgz",
+      "integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==",
+      "devOptional": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "undici-types": "~6.21.0"
+      }
+    },
+    "server/node_modules/ansis": {
+      "version": "3.17.0",
+      "resolved": "https://registry.npmmirror.com/ansis/-/ansis-3.17.0.tgz",
+      "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "server/node_modules/dotenv": {
+      "version": "17.2.3",
+      "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-17.2.3.tgz",
+      "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "server/node_modules/ts-node": {
+      "version": "10.9.2",
+      "resolved": "https://registry.npmmirror.com/ts-node/-/ts-node-10.9.2.tgz",
+      "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+      "devOptional": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "@cspotcode/source-map-support": "^0.8.0",
+        "@tsconfig/node10": "^1.0.7",
+        "@tsconfig/node12": "^1.0.7",
+        "@tsconfig/node14": "^1.0.0",
+        "@tsconfig/node16": "^1.0.2",
+        "acorn": "^8.4.1",
+        "acorn-walk": "^8.1.1",
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "v8-compile-cache-lib": "^3.0.1",
+        "yn": "3.1.1"
+      },
+      "bin": {
+        "ts-node": "dist/bin.js",
+        "ts-node-cwd": "dist/bin-cwd.js",
+        "ts-node-esm": "dist/bin-esm.js",
+        "ts-node-script": "dist/bin-script.js",
+        "ts-node-transpile-only": "dist/bin-transpile.js",
+        "ts-script": "dist/bin-script-deprecated.js"
+      },
+      "peerDependencies": {
+        "@swc/core": ">=1.2.50",
+        "@swc/wasm": ">=1.2.50",
+        "@types/node": "*",
+        "typescript": ">=2.7"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "@swc/wasm": {
+          "optional": true
+        }
+      }
+    },
+    "server/node_modules/typeorm": {
+      "version": "0.3.26",
+      "resolved": "https://registry.npmmirror.com/typeorm/-/typeorm-0.3.26.tgz",
+      "integrity": "sha512-o2RrBNn3lczx1qv4j+JliVMmtkPSqEGpG0UuZkt9tCfWkoXKu8MZnjvp2GjWPll1SehwemQw6xrbVRhmOglj8Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@sqltools/formatter": "^1.2.5",
+        "ansis": "^3.17.0",
+        "app-root-path": "^3.1.0",
+        "buffer": "^6.0.3",
+        "dayjs": "^1.11.13",
+        "debug": "^4.4.0",
+        "dedent": "^1.6.0",
+        "dotenv": "^16.4.7",
+        "glob": "^10.4.5",
+        "sha.js": "^2.4.11",
+        "sql-highlight": "^6.0.0",
+        "tslib": "^2.8.1",
+        "uuid": "^11.1.0",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "typeorm": "cli.js",
+        "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js",
+        "typeorm-ts-node-esm": "cli-ts-node-esm.js"
+      },
+      "engines": {
+        "node": ">=16.13.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/typeorm"
+      },
+      "peerDependencies": {
+        "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0",
+        "@sap/hana-client": "^2.14.22",
+        "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0",
+        "ioredis": "^5.0.4",
+        "mongodb": "^5.8.0 || ^6.0.0",
+        "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1",
+        "mysql2": "^2.2.5 || ^3.0.1",
+        "oracledb": "^6.3.0",
+        "pg": "^8.5.1",
+        "pg-native": "^3.0.0",
+        "pg-query-stream": "^4.0.0",
+        "redis": "^3.1.1 || ^4.0.0 || ^5.0.14",
+        "reflect-metadata": "^0.1.14 || ^0.2.0",
+        "sql.js": "^1.4.0",
+        "sqlite3": "^5.0.3",
+        "ts-node": "^10.7.0",
+        "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0"
+      },
+      "peerDependenciesMeta": {
+        "@google-cloud/spanner": {
+          "optional": true
+        },
+        "@sap/hana-client": {
+          "optional": true
+        },
+        "better-sqlite3": {
+          "optional": true
+        },
+        "ioredis": {
+          "optional": true
+        },
+        "mongodb": {
+          "optional": true
+        },
+        "mssql": {
+          "optional": true
+        },
+        "mysql2": {
+          "optional": true
+        },
+        "oracledb": {
+          "optional": true
+        },
+        "pg": {
+          "optional": true
+        },
+        "pg-native": {
+          "optional": true
+        },
+        "pg-query-stream": {
+          "optional": true
+        },
+        "redis": {
+          "optional": true
+        },
+        "sql.js": {
+          "optional": true
+        },
+        "sqlite3": {
+          "optional": true
+        },
+        "ts-node": {
+          "optional": true
+        },
+        "typeorm-aurora-data-api-driver": {
+          "optional": true
+        }
+      }
+    },
+    "server/node_modules/typeorm/node_modules/dotenv": {
+      "version": "16.6.1",
+      "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz",
+      "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://dotenvx.com"
+      }
+    },
+    "server/node_modules/undici-types": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz",
+      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+      "devOptional": true,
+      "license": "MIT"
+    },
+    "server/node_modules/uuid": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz",
+      "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/esm/bin/uuid"
+      }
+    },
+    "web": {
+      "name": "simple-kb",
+      "version": "0.0.0",
+      "dependencies": {
+        "@google/genai": "^1.32.0",
+        "@types/react-syntax-highlighter": "^15.5.13",
+        "html2canvas": "^1.4.1",
+        "lucide-react": "^0.556.0",
+        "mermaid": "^11.12.2",
+        "pdfjs-dist": "^4.10.38",
+        "react": "^19.2.1",
+        "react-dom": "^19.2.1",
+        "react-markdown": "^10.1.0",
+        "react-syntax-highlighter": "^16.1.0",
+        "rehype-katex": "^7.0.1",
+        "remark-gfm": "^4.0.1",
+        "remark-math": "^6.0.0"
+      },
+      "devDependencies": {
+        "@tailwindcss/typography": "^0.5.19",
+        "@types/node": "^22.14.0",
+        "@vitejs/plugin-react": "^5.0.0",
+        "autoprefixer": "^10.4.23",
+        "postcss": "^8.5.6",
+        "tailwindcss": "^3.4.17",
+        "typescript": "~5.8.2",
+        "vite": "^6.2.0"
+      }
+    },
+    "web/node_modules/@types/node": {
+      "version": "22.19.2",
+      "resolved": "https://registry.npmmirror.com/@types/node/-/node-22.19.2.tgz",
+      "integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==",
+      "dev": true,
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "undici-types": "~6.21.0"
+      }
+    },
+    "web/node_modules/arg": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+      "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "web/node_modules/chokidar": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "web/node_modules/chokidar/node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "license": "ISC",
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "web/node_modules/postcss-selector-parser": {
+      "version": "6.1.2",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+      "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "util-deprecate": "^1.0.2"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "web/node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "web/node_modules/readdirp/node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "web/node_modules/tailwindcss": {
+      "version": "3.4.17",
+      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
+      "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@alloc/quick-lru": "^5.2.0",
+        "arg": "^5.0.2",
+        "chokidar": "^3.6.0",
+        "didyoumean": "^1.2.2",
+        "dlv": "^1.1.3",
+        "fast-glob": "^3.3.2",
+        "glob-parent": "^6.0.2",
+        "is-glob": "^4.0.3",
+        "jiti": "^1.21.6",
+        "lilconfig": "^3.1.3",
+        "micromatch": "^4.0.8",
+        "normalize-path": "^3.0.0",
+        "object-hash": "^3.0.0",
+        "picocolors": "^1.1.1",
+        "postcss": "^8.4.47",
+        "postcss-import": "^15.1.0",
+        "postcss-js": "^4.0.1",
+        "postcss-load-config": "^4.0.2",
+        "postcss-nested": "^6.2.0",
+        "postcss-selector-parser": "^6.1.2",
+        "resolve": "^1.22.8",
+        "sucrase": "^3.35.0"
+      },
+      "bin": {
+        "tailwind": "lib/cli.js",
+        "tailwindcss": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "web/node_modules/typescript": {
+      "version": "5.8.3",
+      "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.8.3.tgz",
+      "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "web/node_modules/undici-types": {
+      "version": "6.21.0",
+      "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz",
+      "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "web/node_modules/vite": {
+      "version": "6.4.1",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-6.4.1.tgz",
+      "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.25.0",
+        "fdir": "^6.4.4",
+        "picomatch": "^4.0.2",
+        "postcss": "^8.5.3",
+        "rollup": "^4.34.9",
+        "tinyglobby": "^0.2.13"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+        "jiti": ">=1.21.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.16.0",
+        "tsx": "^4.8.1",
+        "yaml": "^2.4.2"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "jiti": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        },
+        "tsx": {
+          "optional": true
+        },
+        "yaml": {
+          "optional": true
+        }
+      }
+    }
+  }
+}

+ 15 - 0
package.json

@@ -0,0 +1,15 @@
+{
+  "name": "lumina",
+  "private": true,
+  "workspaces": [
+    "web",
+    "server"
+  ],
+  "scripts": {
+    "dev": "concurrently \"yarn workspace web dev\" \"yarn workspace server start:dev\"",
+    "install:all": "yarn install"
+  },
+  "devDependencies": {
+    "concurrently": "^8.2.2"
+  }
+}

+ 12 - 0
server/.dockerignore

@@ -0,0 +1,12 @@
+node_modules
+dist
+build
+*.log
+.env.local
+.env.development
+.env.test
+coverage
+.nyc_output
+test
+*.test.ts
+*.spec.ts!/uploads/

+ 44 - 0
server/.env.sample

@@ -0,0 +1,44 @@
+PORT=3001
+# 重要:在生产环境中必须更改以下设置
+# 数据库路径
+DATABASE_PATH=./data/metadata.db
+
+# 服务主机配置(生产环境请使用实际服务器地址)
+ELASTICSEARCH_HOST=http://127.0.0.1:9200    # 生产环境请更改
+TIKA_HOST=http://127.0.0.1:9998            # 生产环境请更改
+LIBREOFFICE_URL=http://127.0.0.1:8100       # 生产环境请更改
+JWT_SECRET=your-super-secure-jwt-secret-key-change-it-in-production
+
+# File Upload Configuration
+UPLOAD_FILE_PATH=./uploads
+MAX_FILE_SIZE=104857600
+TEMP_DIR=./temp
+
+# Vector Dimensions Configuration
+# 埋め込みモデルの出力次元数と一致させる必要があります
+# 一般的な値: 2560 (text-embedding-3-large), 1536 (text-embedding-3-small), 2048 (カスタム)
+DEFAULT_VECTOR_DIMENSIONS=2048
+
+# Chunk Size Limits Configuration
+# チャンクサイズの上限 (tokens) - 環境変数による制限(優先度最高)
+# 使用する埋め込みモデルに合わせて設定してください
+# OpenAI text-embedding-3-large: 8191
+# OpenAI text-embedding-3-small: 8191
+# Google Gemini embedding-001: 2048
+MAX_CHUNK_SIZE=8191
+
+# チャンク重なり(オーバーラップ)の上限 (tokens) - 環境変数による制限
+# チャンクサイズの 10-20% を推奨します
+MAX_OVERLAP_SIZE=200
+
+# Memory Management Configuration
+# メモリ使用量の上限 (MB)。この値を超えると待機や強制GCがトリガーされます
+MAX_MEMORY_USAGE_MB=1024
+# バッチ処理サイズ (チャンク数)
+CHUNK_BATCH_SIZE=100
+# 強制GCのしきい値 (MB)
+GC_THRESHOLD_MB=800
+
+# 前端設定
+# ALLOWED_HOSTS - API 接続を許可するホストリスト(カンマ区切り)
+# フロントエンドの .env ファイルで REACT_APP_ALLOWED_HOSTS として設定

+ 4 - 0
server/.prettierrc

@@ -0,0 +1,4 @@
+{
+  "singleQuote": true,
+  "trailingComma": "all"
+}

+ 26 - 0
server/Dockerfile

@@ -0,0 +1,26 @@
+FROM node:20-alpine
+
+WORKDIR /app
+
+# Set apk mirror and install build tools for native modules
+RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
+    apk add --no-cache python3 make g++ imagemagick poppler-utils ghostscript
+
+# Copy package files
+COPY package*.json yarn.lock* ./
+
+# Set yarn registry and install all dependencies (including dev for build)
+RUN yarn config set registry https://registry.npmmirror.com && \
+    yarn install
+
+# Copy source code
+COPY . .
+
+# Build the application
+RUN yarn build
+
+# Expose port
+EXPOSE 3001
+
+# Start application
+CMD ["node", "/app/dist/src/main.js"]

+ 113 - 0
server/README.md

@@ -0,0 +1,113 @@
+# Simple Knowledge Base - バックエンドサービス (Server)
+
+[NestJS](https://nestjs.com/) フレームワークで構築された RAG (検索拡張生成) ナレッジベースのバックエンドシステムです。ファイルの解析、ベクトルインデックス、ハイブリッド検索、およびマルチモデル管理機能を提供します。
+
+## 🌟 主な特徴
+
+- **インテリジェントなドキュメント処理**: [Apache Tika](https://tika.apache.org/) を統合し、PDF、Word、Markdown、TXT など多様な形式からのテキスト抽出をサポート。
+- **効率的なベクトル検索**: [Elasticsearch](https://www.elastic.co/) をベクトルデータベースとして使用。KNN ベクトル検索と全文検索を組み合わせたハイブリッド検索 (Hybrid Search) をサポート。
+- **柔軟な RAG エンジン**: LangChain をベースに構築。チャンク分割ルール (Chunking) や再ランキング (Rerank) のカスタマイズが可能。
+- **マルチモデルプロバイダー**: OpenAI、Google Gemini、およびローカルデプロイの LLM モデルへの動的な接続をサポート。
+- **安全な管理機能**: JWT による認証とユーザー権限管理機能を内蔵。
+
+## 🛠️ 技術スタック
+
+- **フレームワーク**: NestJS (TypeScript)
+- **データベース**: SQLite (TypeORM)
+- **検索エンジン**: Elasticsearch 8.x/9.x
+- **AI フレームワーク**: LangChain
+- **ライブラリ**: RxJS, Class-Validator
+
+## 📋 前提条件
+
+プロジェクトを実行する前に、以下の環境が整っていることを確認してください:
+
+- [Node.js](https://nodejs.org/) (v18 以上推奨)
+- [Yarn](https://yarnpkg.com/)
+- [Docker](https://www.docker.com/) & Docker Compose (インフラ実行用)
+
+## 🚀 クイックスタート
+
+### 1. インフラストラクチャの起動
+
+プロジェクトのルートディレクトリ (`simple-kb/`) にある `docker-compose.yml` ファイルを使用して、Elasticsearch と Tika をクイック起動します。
+
+```bash
+# プロジェクトのルートディレクトリで実行
+docker-compose up -d
+```
+
+起動成功後:
+
+- **Elasticsearch**: ポート `19200` をリスン (コンテナの 9200 ポートをマッピング)
+- **Tika**: ポート `9998` をリスン
+
+### 2. 依存関係のインストール
+
+`server` ディレクトリに移動し、パッケージをインストールします:
+
+```bash
+cd server
+yarn install
+```
+
+### 3. 環境設定
+
+プロジェクトは、基本設定を環境変数のファイルに基づきます。設定(特に Elasticsearch のアドレス)が正しいことを確認してください:
+
+```env
+# データベースのパス
+DATABASE_PATH=server/data/metadata.db
+
+# JWT シークレット
+JWT_SECRET=your_secure_secret
+
+# Elasticsearch 設定 (docker-compose のポートと一致させてください)
+ELASTICSEARCH_HOST=http://localhost:19200
+ELASTICSEARCH_INDEX=knowledge_base
+
+# Tika 設定
+TIKA_HOST=http://localhost:9998
+
+# ファイルアップロードの保存パス
+UPLOAD_FILE_PATH=./uploads
+```
+
+### 4. サービスの起動
+
+```bash
+# 開発モード (推奨。ホットリロード対応)
+yarn run start:dev
+
+# 本番モードでのビルドと実行
+yarn build
+yarn run start:prod
+```
+
+バックエンドサービスはデフォルトで **<http://localhost:13000>** で実行され、API プレフィックスは `/api` です。
+
+## 🧪 テスト
+
+```bash
+# ユニットテスト
+yarn run test
+
+# E2E テスト
+yarn run test:e2e
+```
+
+## ⚠️ 注意事項とヒント
+
+1. **データベースの初期化**:
+   - 初回実行時に、TypeORM は `server/data/` ディレクトリ配下に `metadata.db` (または設定された DB) を自動的に作成します。
+   - 開発環境では `synchronize: true` により、テーブル構造が自動同期されます。
+
+2. **Elasticsearch への接続**:
+   - `Connection refused` エラーが発生した場合は、Docker コンテナが正常に動作しているか確認してください (`docker ps`)。
+   - サービス起動時に、`knowledge_base` という名前のインデックスが自動的に検出・作成されます。
+
+3. **デフォルトのアカウント**:
+   - データベースをリセットした場合は、新規ユーザー登録を行うか、既存の管理データを参照してください。通常はフロントエンドの登録機能を使用して最初のユーザーを作成することをお勧めします。
+
+4. **ファイルの解析**:
+   - 大容量のファイルをアップロードした場合、Tika による解析に数秒かかることがあります。フロントエンドの処理ステータスを確認しながらお待ちください。

BIN
server/chi_sim.traineddata


BIN
server/eng.traineddata


+ 41 - 0
server/eslint.config.mjs

@@ -0,0 +1,41 @@
+// @ts-check
+import eslint from '@eslint/js';
+import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
+import globals from 'globals';
+import tseslint from 'typescript-eslint';
+
+export default tseslint.config(
+  {
+    ignores: ['eslint.config.mjs'],
+  },
+  eslint.configs.recommended,
+  ...tseslint.configs.recommendedTypeChecked,
+  eslintPluginPrettierRecommended,
+  {
+    languageOptions: {
+      globals: {
+        ...globals.node,
+        ...globals.jest,
+      },
+      sourceType: 'commonjs',
+      parserOptions: {
+        projectService: true,
+        tsconfigRootDir: import.meta.dirname,
+      },
+    },
+  },
+  {
+    rules: {
+      '@typescript-eslint/no-explicit-any': 'off',
+      '@typescript-eslint/no-floating-promises': 'warn',
+      '@typescript-eslint/no-unsafe-argument': 'warn',
+      'prettier/prettier': ['error', { endOfLine: 'auto' }],
+      '@typescript-eslint/no-unsafe-assignment': 'off',
+      '@typescript-eslint/no-unsafe-call': 'off',
+      '@typescript-eslint/no-unsafe-member-access': 'off',
+      '@typescript-eslint/no-unsafe-return': 'off',
+      // "@pertti"
+
+    },
+  },
+);

BIN
server/jpn.traineddata


+ 8 - 0
server/nest-cli.json

@@ -0,0 +1,8 @@
+{
+  "$schema": "https://json.schemastore.org/nest-cli",
+  "collection": "@nestjs/schematics",
+  "sourceRoot": "src",
+  "compilerOptions": {
+    "deleteOutDir": true
+  }
+}

+ 104 - 0
server/package.json

@@ -0,0 +1,104 @@
+{
+  "name": "lumina-server",
+  "version": "0.0.1",
+  "description": "",
+  "author": "",
+  "private": true,
+  "license": "UNLICENSED",
+  "scripts": {
+    "build": "nest build",
+    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
+    "start": "nest start",
+    "start:dev": "nest start --watch",
+    "start:debug": "nest start --debug --watch",
+    "start:prod": "node dist/main",
+    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
+    "test": "jest",
+    "test:watch": "jest --watch",
+    "test:cov": "jest --coverage",
+    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
+    "test:e2e": "jest --config ./test/jest-e2e.json"
+  },
+  "dependencies": {
+    "@elastic/elasticsearch": "^9.2.0",
+    "@langchain/core": "^1.1.5",
+    "@langchain/openai": "^1.1.3",
+    "@langchain/textsplitters": "^1.0.1",
+    "@nestjs/common": "^11.0.1",
+    "@nestjs/config": "^4.0.2",
+    "@nestjs/core": "^11.0.1",
+    "@nestjs/jwt": "^11.0.2",
+    "@nestjs/mapped-types": "^2.1.0",
+    "@nestjs/passport": "^11.0.5",
+    "@nestjs/platform-express": "^11.0.1",
+    "@nestjs/schedule": "^6.1.0",
+    "@nestjs/serve-static": "^5.0.4",
+    "@nestjs/typeorm": "^11.0.0",
+    "@types/cron": "^2.0.1",
+    "axios": "^1.13.2",
+    "bcrypt": "^6.0.0",
+    "better-sqlite3": "^12.5.0",
+    "class-transformer": "^0.5.1",
+    "class-validator": "^0.14.3",
+    "dotenv": "^17.2.3",
+    "form-data": "^4.0.5",
+    "langchain": "^1.1.5",
+    "node-edge-tts": "^1.2.8",
+    "passport": "^0.7.0",
+    "passport-jwt": "^4.0.1",
+    "pdf-lib": "^1.17.1",
+    "pdf2image": "^1.2.3",
+    "reflect-metadata": "^0.2.2",
+    "rxjs": "^7.8.1",
+    "tesseract.js": "^7.0.0",
+    "typeorm": "0.3.26"
+  },
+  "devDependencies": {
+    "@eslint/eslintrc": "^3.2.0",
+    "@eslint/js": "^9.18.0",
+    "@nestjs/cli": "^11.0.0",
+    "@nestjs/schematics": "^11.0.0",
+    "@nestjs/testing": "^11.0.1",
+    "@types/bcrypt": "^6.0.0",
+    "@types/better-sqlite3": "^7.6.13",
+    "@types/express": "^5.0.0",
+    "@types/jest": "^30.0.0",
+    "@types/multer": "^2.0.0",
+    "@types/node": "^22.10.7",
+    "@types/passport-jwt": "^4.0.1",
+    "@types/passport-local": "^1.0.38",
+    "@types/supertest": "^6.0.2",
+    "eslint": "^9.18.0",
+    "eslint-config-prettier": "^10.0.1",
+    "eslint-plugin-prettier": "^5.2.2",
+    "globals": "^16.0.0",
+    "jest": "^30.0.0",
+    "passport-local": "^1.0.0",
+    "prettier": "^3.4.2",
+    "source-map-support": "^0.5.21",
+    "supertest": "^7.0.0",
+    "ts-jest": "^29.2.5",
+    "ts-loader": "^9.5.2",
+    "ts-node": "^10.9.2",
+    "tsconfig-paths": "^4.2.0",
+    "typescript": "^5.7.3",
+    "typescript-eslint": "^8.20.0"
+  },
+  "jest": {
+    "moduleFileExtensions": [
+      "js",
+      "json",
+      "ts"
+    ],
+    "rootDir": "src",
+    "testRegex": ".*\\.spec\\.ts$",
+    "transform": {
+      "^.+\\.(t|j)s$": "ts-jest"
+    },
+    "collectCoverageFrom": [
+      "**/*.(t|j)s"
+    ],
+    "coverageDirectory": "../coverage",
+    "testEnvironment": "node"
+  }
+}

+ 60 - 0
server/pdf_to_images.py

@@ -0,0 +1,60 @@
+import fitz  # PyMuPDF
+import sys
+import os
+import json
+
+def convert_pdf_to_images(pdf_path, output_dir, zoom=2.0, quality=85):
+    """
+    Converts PDF pages to images.
+    zoom: 2.0 means 200% scaling (approx 144 DPI if original is 72 DPI)
+    """
+    try:
+        if not os.path.exists(output_dir):
+            os.makedirs(output_dir)
+
+        doc = fitz.open(pdf_path)
+        images = []
+        
+        # Matrix for scaling (DPI control)
+        mat = fitz.Matrix(zoom, zoom)
+        
+        for i in range(len(doc)):
+            page = doc.load_page(i)
+            pix = page.get_pixmap(matrix=mat, colorspace=fitz.csRGB)
+            
+            output_path = os.path.join(output_dir, f"page-{i+1}.jpg")
+            # In newer PyMuPDF, save() doesn't take quality. Use tobytes instead.
+            img_bytes = pix.tobytes("jpg", jpg_quality=quality)
+            with open(output_path, "wb") as f:
+                f.write(img_bytes)
+            
+            images.append({
+                "path": output_path,
+                "pageIndex": i + 1,
+                "size": os.path.getsize(output_path)
+            })
+            
+        doc.close()
+        return {
+            "success": True,
+            "images": images,
+            "totalPages": len(images)
+        }
+    except Exception as e:
+        return {
+            "success": False,
+            "error": str(e)
+        }
+
+if __name__ == "__main__":
+    if len(sys.argv) < 3:
+        print(json.dumps({"success": False, "error": "Usage: python pdf_to_images.py <pdf_path> <output_dir> [zoom] [quality]"}))
+        sys.exit(1)
+        
+    pdf_path = sys.argv[1]
+    output_dir = sys.argv[2]
+    zoom = float(sys.argv[3]) if len(sys.argv) > 3 else 2.0
+    quality = int(sys.argv[4]) if len(sys.argv) > 4 else 85
+    
+    result = convert_pdf_to_images(pdf_path, output_dir, zoom, quality)
+    print(json.dumps(result))

+ 8 - 0
server/src/ai/ai.module.ts

@@ -0,0 +1,8 @@
+import { Module } from '@nestjs/common';
+import { EmbeddingService } from './embedding.service';
+
+@Module({
+  providers: [EmbeddingService],
+  exports: [EmbeddingService],
+})
+export class AiModule {}

+ 32 - 0
server/src/ai/embedding.service.ts

@@ -0,0 +1,32 @@
+import { Injectable, Logger } from '@nestjs/common';
+import { OpenAIEmbeddings } from '@langchain/openai';
+import { ModelConfig } from '../model-config/model-config.entity';
+
+@Injectable()
+export class EmbeddingService {
+  private readonly logger = new Logger(EmbeddingService.name);
+
+  async getEmbeddings(text: string, config: ModelConfig): Promise<number[]> {
+    try {
+      // ほとんどの設定が OpenAI インターフェースと互換性があると仮定
+      const embeddings = new OpenAIEmbeddings({
+        openAIApiKey: config.apiKey || 'sk-placeholder', // ローカルモデルの場合は key が不要な場合がある
+        configuration: {
+          baseURL: config.baseUrl,
+        },
+        modelName: config.modelId, // modelId に修正
+      });
+
+      // テキストが長すぎる問題の処理?LangChain は通常、自動的に処理するかエラーを出力します。
+      // ここでは簡略化し、直接呼び出します
+      const vector = await embeddings.embedQuery(text);
+      return vector;
+    } catch (error) {
+      this.logger.error(
+        `Failed to generate embeddings using model ${config.modelId}`,
+        error,
+      ); // modelId に修正
+      throw error;
+    }
+  }
+}

+ 73 - 0
server/src/api/api.controller.ts

@@ -0,0 +1,73 @@
+import {
+  Body,
+  Controller,
+  Get,
+  HttpCode,
+  HttpStatus,
+  Post,
+  Request,
+  UseGuards,
+} from '@nestjs/common';
+import { ApiService } from './api.service';
+import { JwtAuthGuard } from '../auth/jwt-auth.guard';
+import { ModelConfigService } from '../model-config/model-config.service';
+
+class ChatDto {
+  prompt: string;
+}
+
+@Controller()
+export class ApiController {
+  constructor(
+    private readonly apiService: ApiService,
+    private readonly modelConfigService: ModelConfigService,
+  ) { }
+
+  @Get('health')
+  healthCheck() {
+    return this.apiService.healthCheck();
+  }
+
+  @Post('chat')
+  @UseGuards(JwtAuthGuard)
+  @HttpCode(HttpStatus.OK)
+  async chat(@Request() req, @Body() chatDto: ChatDto) {
+    const { prompt } = chatDto;
+    if (!prompt) {
+      throw new Error('Prompt is required');
+    }
+
+    try {
+      // ユーザーの LLM モデル設定を取得
+      const models = await this.modelConfigService.findAll(req.user.id);
+      const llmModel = models.find((m) => m.type === 'llm');
+
+      if (!llmModel) {
+        throw new Error('システム設定で LLM モデルを追加してください');
+      }
+
+      if (!llmModel.apiKey) {
+        throw new Error('LLM モデルで API キーを設定してください');
+      }
+
+      // entity タイプを types インターフェースに変換
+      const modelConfigForService = {
+        id: llmModel.id,
+        name: llmModel.name,
+        modelId: llmModel.modelId,
+        baseUrl: llmModel.baseUrl,
+        apiKey: llmModel.apiKey,
+        type: llmModel.type as any,
+        supportsVision: llmModel.supportsVision,
+      };
+
+      const response = await this.apiService.getChatCompletion(
+        prompt,
+        modelConfigForService,
+      );
+      return { response };
+    } catch (error) {
+      throw new Error(error.message || 'サーバー内部エラー');
+    }
+  }
+}

+ 19 - 0
server/src/api/api.module.ts

@@ -0,0 +1,19 @@
+import { Module } from '@nestjs/common';
+import { ApiController } from './api.controller';
+import { ApiService } from './api.service';
+import { KnowledgeBaseModule } from '../knowledge-base/knowledge-base.module';
+import { AuthModule } from '../auth/auth.module';
+import { ModelConfigModule } from '../model-config/model-config.module'; // Added
+import { UserSettingModule } from '../user-setting/user-setting.module'; // Added
+
+@Module({
+  imports: [
+    KnowledgeBaseModule,
+    AuthModule,
+    ModelConfigModule,
+    UserSettingModule,
+  ],
+  controllers: [ApiController],
+  providers: [ApiService],
+})
+export class ApiModule {}

+ 45 - 0
server/src/api/api.service.ts

@@ -0,0 +1,45 @@
+import { Injectable } from '@nestjs/common';
+import { ChatOpenAI } from '@langchain/openai';
+import { ModelConfig } from '../types';
+
+@Injectable()
+export class ApiService {
+  constructor() { }
+
+  // 簡易的なヘルスチェックメソッド
+  healthCheck() {
+    return { status: 'ok', message: 'API is healthy' };
+  }
+
+  async getChatCompletion(
+    prompt: string,
+    modelConfig: ModelConfig,
+  ): Promise<string> {
+    if (!modelConfig.apiKey) {
+      throw new Error('API key is required');
+    }
+
+    try {
+      const llm = this.createLLM(modelConfig);
+      const response = await llm.invoke(prompt);
+      return response.content.toString();
+    } catch (error) {
+      console.error('LangChain call failed:', error);
+      if (error.message?.includes('401')) {
+        throw new Error('Invalid API key');
+      }
+      throw new Error('Failed to get response: ' + error.message);
+    }
+  }
+
+  private createLLM(modelConfig: ModelConfig): ChatOpenAI {
+    return new ChatOpenAI({
+      temperature: 0.7,
+      apiKey: modelConfig.apiKey,
+      modelName: modelConfig.modelId,
+      configuration: {
+        baseURL: modelConfig.baseUrl || 'https://api.openai.com/v1',
+      },
+    });
+  }
+}

+ 22 - 0
server/src/app.controller.spec.ts

@@ -0,0 +1,22 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { AppController } from './app.controller';
+import { AppService } from './app.service';
+
+describe('AppController', () => {
+  let appController: AppController;
+
+  beforeEach(async () => {
+    const app: TestingModule = await Test.createTestingModule({
+      controllers: [AppController],
+      providers: [AppService],
+    }).compile();
+
+    appController = app.get<AppController>(AppController);
+  });
+
+  describe('root', () => {
+    it('should return "Hello World!"', () => {
+      expect(appController.getHello()).toBe('Hello World!');
+    });
+  });
+});

+ 12 - 0
server/src/app.controller.ts

@@ -0,0 +1,12 @@
+import { Controller, Get } from '@nestjs/common';
+import { AppService } from './app.service';
+
+@Controller()
+export class AppController {
+  constructor(private readonly appService: AppService) {}
+
+  @Get()
+  getHello(): string {
+    return this.appService.getHello();
+  }
+}

+ 104 - 0
server/src/app.module.ts

@@ -0,0 +1,104 @@
+import { Module } from '@nestjs/common';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { ScheduleModule } from '@nestjs/schedule';
+import { ServeStaticModule } from '@nestjs/serve-static';
+import { join } from 'path';
+import { APP_GUARD } from '@nestjs/core';
+import { AppController } from './app.controller';
+import { AppService } from './app.service';
+import { ApiModule } from './api/api.module';
+import { ElasticsearchModule } from './elasticsearch/elasticsearch.module';
+import { UploadModule } from './upload/upload.module';
+import { ChatModule } from './chat/chat.module';
+import { AuthModule } from './auth/auth.module';
+import { I18nModule } from './i18n/i18n.module';
+import { JwtAuthGuard } from './auth/jwt-auth.guard';
+import { KnowledgeBaseModule } from './knowledge-base/knowledge-base.module';
+import { ModelConfigModule } from './model-config/model-config.module';
+import { UserModule } from './user/user.module';
+import { UserSettingModule } from './user-setting/user-setting.module';
+import { TikaModule } from './tika/tika.module';
+import { VisionModule } from './vision/vision.module';
+import { LibreOfficeModule } from './libreoffice/libreoffice.module';
+import { Pdf2ImageModule } from './pdf2image/pdf2image.module';
+import { VisionPipelineModule } from './vision-pipeline/vision-pipeline.module';
+import { KnowledgeGroupModule } from './knowledge-group/knowledge-group.module';
+import { SearchHistoryModule } from './search-history/search-history.module';
+import { NoteModule } from './note/note.module';
+import { PodcastModule } from './podcasts/podcast.module';
+import { ImportTaskModule } from './import-task/import-task.module';
+import { User } from './user/user.entity';
+import { UserSetting } from './user-setting/user-setting.entity';
+import { ModelConfig } from './model-config/model-config.entity';
+import { KnowledgeBase } from './knowledge-base/knowledge-base.entity';
+import { KnowledgeGroup } from './knowledge-group/knowledge-group.entity';
+import { SearchHistory } from './search-history/search-history.entity';
+import { ChatMessage } from './search-history/chat-message.entity';
+import { Note } from './note/note.entity';
+import { PodcastEpisode } from './podcasts/entities/podcast-episode.entity';
+import { ImportTask } from './import-task/import-task.entity';
+
+@Module({
+  imports: [
+    ConfigModule.forRoot({
+      isGlobal: true,
+      ignoreEnvFile: false,
+    }),
+    ServeStaticModule.forRoot({
+      rootPath: join(process.cwd(), 'uploads'),
+      serveRoot: '/uploads',
+    }),
+    ScheduleModule.forRoot(),
+    TypeOrmModule.forRootAsync({
+      imports: [ConfigModule],
+      inject: [ConfigService],
+      useFactory: (configService: ConfigService) => ({
+        type: 'better-sqlite3',
+        database: configService.get<string>('DATABASE_PATH'),
+        entities: [
+          User,
+          UserSetting,
+          ModelConfig,
+          KnowledgeBase,
+          KnowledgeGroup,
+          SearchHistory,
+          ChatMessage,
+          Note,
+          PodcastEpisode,
+          ImportTask,
+        ],
+        synchronize: true, // Auto-create database schema. Disable in production.
+      }),
+    }),
+    AuthModule,
+    I18nModule,
+    UserModule,
+    UserSettingModule,
+    ModelConfigModule,
+    KnowledgeBaseModule,
+    KnowledgeGroupModule,
+    SearchHistoryModule,
+    NoteModule,
+    PodcastModule,
+    TikaModule,
+    VisionModule,
+    LibreOfficeModule,
+    Pdf2ImageModule,
+    VisionPipelineModule,
+    ApiModule,
+    ElasticsearchModule,
+    UploadModule,
+    ChatModule,
+    ImportTaskModule,
+  ],
+  controllers: [AppController],
+  providers: [
+    AppService,
+    {
+      provide: APP_GUARD,
+      useClass: JwtAuthGuard,
+    },
+  ],
+})
+export class AppModule { }

+ 8 - 0
server/src/app.service.ts

@@ -0,0 +1,8 @@
+import { Injectable } from '@nestjs/common';
+
+@Injectable()
+export class AppService {
+  getHello(): string {
+    return 'Hello World!';
+  }
+}

+ 15 - 0
server/src/auth/admin.guard.ts

@@ -0,0 +1,15 @@
+import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
+import { Observable } from 'rxjs';
+
+@Injectable()
+export class AdminGuard implements CanActivate {
+  canActivate(
+    context: ExecutionContext,
+  ): boolean | Promise<boolean> | Observable<boolean> {
+    const request = context.switchToHttp().getRequest();
+    const user = request.user;
+
+    // Check if user exists and has admin privileges
+    return user && user.isAdmin === true;
+  }
+}

+ 23 - 0
server/src/auth/auth.controller.ts

@@ -0,0 +1,23 @@
+import { Controller, Get, Post, Request, UseGuards } from '@nestjs/common';
+import { AuthService } from './auth.service';
+import { LocalAuthGuard } from './local-auth.guard';
+import { JwtAuthGuard } from './jwt-auth.guard';
+import { Public } from './public.decorator';
+
+@Controller('auth')
+export class AuthController {
+  constructor(private authService: AuthService) { }
+
+  @Public()
+  @UseGuards(LocalAuthGuard)
+  @Post('login')
+  async login(@Request() req) {
+    return this.authService.login(req.user);
+  }
+
+  @UseGuards(JwtAuthGuard)
+  @Get('profile')
+  getProfile(@Request() req) {
+    return req.user;
+  }
+}

+ 27 - 0
server/src/auth/auth.module.ts

@@ -0,0 +1,27 @@
+import { Module } from '@nestjs/common';
+import { AuthService } from './auth.service';
+import { AuthController } from './auth.controller';
+import { UserModule } from '../user/user.module';
+import { PassportModule } from '@nestjs/passport';
+import { JwtModule } from '@nestjs/jwt';
+import { ConfigModule, ConfigService } from '@nestjs/config';
+import { LocalStrategy } from './local.strategy';
+import { JwtStrategy } from './jwt.strategy';
+
+@Module({
+  imports: [
+    UserModule,
+    PassportModule,
+    JwtModule.registerAsync({
+      imports: [ConfigModule],
+      inject: [ConfigService],
+      useFactory: (configService: ConfigService) => ({
+        secret: configService.get<string>('JWT_SECRET'),
+        signOptions: { expiresIn: '1d' }, // Token expires in 1 day
+      }),
+    }),
+  ],
+  providers: [AuthService, LocalStrategy, JwtStrategy],
+  controllers: [AuthController],
+})
+export class AuthModule {}

+ 28 - 0
server/src/auth/auth.service.ts

@@ -0,0 +1,28 @@
+import { Injectable } from '@nestjs/common';
+import { UserService } from '../user/user.service';
+import { JwtService } from '@nestjs/jwt';
+import { User } from '../user/user.entity';
+import { SafeUser } from '../user/dto/user-safe.dto';
+
+@Injectable()
+export class AuthService {
+  constructor(
+    private userService: UserService,
+    private jwtService: JwtService,
+  ) {}
+
+  async validateUser(username: string, pass: string): Promise<User | null> {
+    const user = await this.userService.findOneByUsername(username);
+    if (user && (await user.validatePassword(pass))) {
+      return user;
+    }
+    return null;
+  }
+
+  async login(user: SafeUser) {
+    const payload = { username: user.username, sub: user.id };
+    return {
+      access_token: this.jwtService.sign(payload),
+    };
+  }
+}

+ 22 - 0
server/src/auth/jwt-auth.guard.ts

@@ -0,0 +1,22 @@
+import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
+import { Reflector } from '@nestjs/core';
+import { AuthGuard } from '@nestjs/passport';
+import { IS_PUBLIC_KEY } from './public.decorator';
+
+@Injectable()
+export class JwtAuthGuard extends AuthGuard('jwt') implements CanActivate {
+  constructor(private reflector: Reflector) {
+    super();
+  }
+
+  canActivate(context: ExecutionContext) {
+    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
+      context.getHandler(),
+      context.getClass(),
+    ]);
+    if (isPublic) {
+      return true;
+    }
+    return super.canActivate(context);
+  }
+}

+ 33 - 0
server/src/auth/jwt.strategy.ts

@@ -0,0 +1,33 @@
+import { Injectable } from '@nestjs/common';
+import { PassportStrategy } from '@nestjs/passport';
+import { ExtractJwt, Strategy } from 'passport-jwt';
+import { ConfigService } from '@nestjs/config';
+import { UserService } from '../user/user.service';
+import { SafeUser } from '../user/dto/user-safe.dto'; // Import SafeUser
+
+@Injectable()
+export class JwtStrategy extends PassportStrategy(Strategy) {
+  constructor(
+    private configService: ConfigService,
+    private userService: UserService,
+  ) {
+    super({
+      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
+      ignoreExpiration: false,
+      secretOrKey: configService.get<string>('JWT_SECRET')!,
+    });
+  }
+
+  // Passport first verifies the JWT's signature and expiration, then calls this method.
+  async validate(payload: {
+    sub: string;
+    username: string;
+  }): Promise<SafeUser | null> {
+    const user = await this.userService.findOneByUsername(payload.username);
+    if (user) {
+      const { password, ...result } = user;
+      return result as SafeUser;
+    }
+    return null;
+  }
+}

+ 5 - 0
server/src/auth/local-auth.guard.ts

@@ -0,0 +1,5 @@
+import { Injectable } from '@nestjs/common';
+import { AuthGuard } from '@nestjs/passport';
+
+@Injectable()
+export class LocalAuthGuard extends AuthGuard('local') {}

+ 21 - 0
server/src/auth/local.strategy.ts

@@ -0,0 +1,21 @@
+import { Strategy } from 'passport-local';
+import { PassportStrategy } from '@nestjs/passport';
+import { Injectable, UnauthorizedException } from '@nestjs/common';
+import { AuthService } from './auth.service';
+import { SafeUser } from '../user/dto/user-safe.dto'; // Import SafeUser
+
+@Injectable()
+export class LocalStrategy extends PassportStrategy(Strategy) {
+  constructor(private authService: AuthService) {
+    super({ usernameField: 'username' });
+  }
+
+  async validate(username: string, password: string): Promise<SafeUser> {
+    const user = await this.authService.validateUser(username, password);
+    if (!user) {
+      throw new UnauthorizedException('Incorrect username or password');
+    }
+    const { password: userPassword, ...result } = user; // Destructure to remove password
+    return result as SafeUser;
+  }
+}

+ 4 - 0
server/src/auth/public.decorator.ts

@@ -0,0 +1,4 @@
+import { SetMetadata } from '@nestjs/common';
+
+export const IS_PUBLIC_KEY = 'isPublic';
+export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

+ 223 - 0
server/src/chat/chat.controller.ts

@@ -0,0 +1,223 @@
+import {
+  Body,
+  Controller,
+  Post,
+  Request,
+  Res,
+  UseGuards,
+} from '@nestjs/common';
+import { Response } from 'express';
+import { ChatMessage, ChatService } from './chat.service';
+import { JwtAuthGuard } from '../auth/jwt-auth.guard';
+import { ModelConfigService } from '../model-config/model-config.service';
+
+class StreamChatDto {
+  message: string;
+  history: ChatMessage[];
+  userLanguage?: string;
+  selectedEmbeddingId?: string;
+  selectedLLMId?: string; // 新增:选中的 LLM 模型 ID
+  selectedGroups?: string[]; // 新增
+  selectedFiles?: string[]; // 新增:选中的文件
+  historyId?: string; // 新增
+  enableRerank?: boolean; // 新增
+  selectedRerankId?: string; // 新增
+  temperature?: number; // 新增:temperature 参数
+  maxTokens?: number; // 新增:maxTokens 参数
+  topK?: number; // 新增:topK 参数
+  similarityThreshold?: number; // 新增:similarityThreshold 参数
+  enableQueryExpansion?: boolean;
+  enableHyDE?: boolean;
+  scoreThreshold?: number;
+}
+
+@Controller('chat')
+@UseGuards(JwtAuthGuard)
+export class ChatController {
+  constructor(
+    private chatService: ChatService,
+    private modelConfigService: ModelConfigService,
+  ) { }
+
+  @Post('stream')
+  async streamChat(
+    @Request() req,
+    @Body() body: StreamChatDto,
+    @Res() res: Response,
+  ) {
+    try {
+      const {
+        message,
+        history = [],
+        userLanguage = 'zh',
+        selectedEmbeddingId,
+        selectedLLMId,
+        selectedGroups,
+        selectedFiles,
+        historyId,
+        enableRerank,
+        selectedRerankId,
+        temperature,
+        maxTokens,
+        topK,
+        similarityThreshold,
+        scoreThreshold,
+        enableQueryExpansion,
+        enableHyDE,
+      } = body;
+      const userId = req.user.id;
+
+      console.log('=== 聊天调试信息 ===');
+      console.log('User ID:', userId);
+      console.log('Message:', message);
+      console.log('User Language:', userLanguage);
+      console.log('Selected Embedding ID:', selectedEmbeddingId);
+      console.log('Selected LLM ID:', selectedLLMId);
+      console.log('Selected Groups:', selectedGroups);
+      console.log('Selected Files:', selectedFiles);
+      console.log('History ID:', historyId);
+      console.log('Temperature:', temperature);
+      console.log('Max Tokens:', maxTokens);
+      console.log('Top K:', topK);
+      console.log('Similarity Threshold:', similarityThreshold);
+      console.log('Query Expansion:', enableQueryExpansion);
+      console.log('HyDE:', enableHyDE);
+
+      // 获取用户的LLM模型配置
+      const models = await this.modelConfigService.findAll(userId);
+
+      let llmModel;
+      if (selectedLLMId) {
+        llmModel = models.find(m => m.id === selectedLLMId && m.type === 'llm' && m.apiKey);
+        if (llmModel) {
+          console.log('使用选中的LLM模型:', llmModel.name);
+        } else {
+          console.warn('未找到选中的LLM模型:', selectedLLMId, '回退到默认');
+        }
+      }
+
+      // Fallback: デフォルトモデルを優先
+      if (!llmModel) {
+        // まずデフォルトモデルを探す
+        llmModel = models.find((m) => m.type === 'llm' && m.apiKey && m.isDefault && m.isEnabled !== false);
+
+        // デフォルトがない場合、有効な最初のモデルを使用(後方互換性)
+        if (!llmModel) {
+          llmModel = models.find((m) => m.type === 'llm' && m.apiKey && m.isEnabled !== false);
+        }
+      }
+
+      console.log('最终使用的LLM模型:', llmModel ? llmModel.name : '无');
+
+      // 设置 SSE 响应头
+      res.setHeader('Content-Type', 'text/event-stream');
+      res.setHeader('Cache-Control', 'no-cache');
+      res.setHeader('Connection', 'keep-alive');
+      res.setHeader('Access-Control-Allow-Origin', '*');
+
+      if (!llmModel) {
+        res.write(
+          `data: ${JSON.stringify({ type: 'error', data: '请在模型管理中添加LLM模型并配置API密钥' })}\n\n`,
+        );
+        res.write('data: [DONE]\n\n');
+        res.end();
+        return;
+      }
+
+      const stream = this.chatService.streamChat(
+        message,
+        history,
+        userId,
+        llmModel as any,
+        userLanguage,
+        selectedEmbeddingId,
+        selectedGroups,
+        selectedFiles,
+        historyId,
+        enableRerank,
+        selectedRerankId,
+        temperature,
+        maxTokens,
+        topK,
+        similarityThreshold,
+        enableQueryExpansion,
+        enableHyDE,
+        scoreThreshold,
+      );
+
+      for await (const chunk of stream) {
+        res.write(`data: ${JSON.stringify(chunk)}\n\n`);
+      }
+
+      res.write('data: [DONE]\n\n');
+      res.end();
+    } catch (error) {
+      console.error('Stream chat error:', error);
+      try {
+        res.write(
+          `data: ${JSON.stringify({ type: 'error', data: error.message || '服务器错误' })}\n\n`,
+        );
+        res.write('data: [DONE]\n\n');
+        res.end();
+      } catch (writeError) {
+        console.error('Failed to write error response:', writeError);
+      }
+    }
+  }
+
+  @Post('assist')
+  async streamAssist(
+    @Request() req,
+    @Body() body: { instruction: string; context: string },
+    @Res() res: Response,
+  ) {
+    try {
+      const { instruction, context } = body;
+      const userId = req.user.id; // Corrected to use req.user.id
+
+      const models = await this.modelConfigService.findAll(userId);
+
+      // デフォルトモデルを優先
+      let llmModel = models.find((m) => m.type === 'llm' && m.apiKey && m.isDefault && m.isEnabled !== false);
+
+      // デフォルトがない場合、有効な最初のモデルを使用
+      if (!llmModel) {
+        llmModel = models.find((m) => m.type === 'llm' && m.apiKey && m.isEnabled !== false);
+      }
+
+      res.setHeader('Content-Type', 'text/event-stream');
+      res.setHeader('Cache-Control', 'no-cache');
+      res.setHeader('Connection', 'keep-alive');
+      res.setHeader('Access-Control-Allow-Origin', '*');
+
+      if (!llmModel) {
+        res.write(
+          `data: ${JSON.stringify({ type: 'error', data: '未找到LLM模型配置' })}\n\n`,
+        );
+        res.write('data: [DONE]\n\n');
+        res.end();
+        return;
+      }
+
+      const stream = this.chatService.streamAssist(
+        instruction,
+        context,
+        llmModel as any,
+      );
+
+      for await (const chunk of stream) {
+        res.write(`data: ${JSON.stringify(chunk)}\n\n`);
+      }
+
+      res.write('data: [DONE]\n\n');
+      res.end();
+    } catch (error) {
+      console.error('Stream assist error:', error);
+      res.write(
+        `data: ${JSON.stringify({ type: 'error', data: error.message || '服务器错误' })}\n\n`,
+      );
+      res.write('data: [DONE]\n\n');
+      res.end();
+    }
+  }
+}

+ 26 - 0
server/src/chat/chat.module.ts

@@ -0,0 +1,26 @@
+import { Module, forwardRef } from '@nestjs/common';
+import { ChatController } from './chat.controller';
+import { ChatService } from './chat.service';
+import { ElasticsearchModule } from '../elasticsearch/elasticsearch.module';
+import { KnowledgeBaseModule } from '../knowledge-base/knowledge-base.module';
+import { ModelConfigModule } from '../model-config/model-config.module';
+import { UserSettingModule } from '../user-setting/user-setting.module';
+import { KnowledgeGroupModule } from '../knowledge-group/knowledge-group.module';
+import { SearchHistoryModule } from '../search-history/search-history.module';
+import { RagModule } from '../rag/rag.module';
+
+@Module({
+  imports: [
+    forwardRef(() => ElasticsearchModule),
+    forwardRef(() => KnowledgeBaseModule),
+    ModelConfigModule,
+    UserSettingModule,
+    forwardRef(() => KnowledgeGroupModule),
+    SearchHistoryModule,
+    forwardRef(() => RagModule),
+  ],
+  controllers: [ChatController],
+  providers: [ChatService],
+  exports: [ChatService],
+})
+export class ChatModule { }

+ 532 - 0
server/src/chat/chat.service.ts

@@ -0,0 +1,532 @@
+import { Injectable, Logger, Inject, forwardRef } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { ChatOpenAI } from '@langchain/openai';
+import { PromptTemplate } from '@langchain/core/prompts';
+import { ElasticsearchService } from '../elasticsearch/elasticsearch.service';
+import { EmbeddingService } from '../knowledge-base/embedding.service';
+import { ModelConfigService } from '../model-config/model-config.service';
+import { KnowledgeGroupService } from '../knowledge-group/knowledge-group.service';
+import { SearchHistoryService } from '../search-history/search-history.service';
+import { ModelConfig } from '../types';
+import { RagService } from '../rag/rag.service';
+
+import { DEFAULT_VECTOR_DIMENSIONS, DEFAULT_LANGUAGE } from '../common/constants';
+import { I18nService } from '../i18n/i18n.service';
+import { UserSettingService } from '../user-setting/user-setting.service';
+
+export interface ChatMessage {
+  role: 'user' | 'assistant';
+  content: string;
+}
+
+@Injectable()
+export class ChatService {
+  private readonly logger = new Logger(ChatService.name);
+  private readonly defaultDimensions: number;
+
+  constructor(
+    @Inject(forwardRef(() => ElasticsearchService))
+    private elasticsearchService: ElasticsearchService,
+    private embeddingService: EmbeddingService,
+    private modelConfigService: ModelConfigService,
+    @Inject(forwardRef(() => KnowledgeGroupService))
+    private knowledgeGroupService: KnowledgeGroupService,
+    private searchHistoryService: SearchHistoryService,
+    private configService: ConfigService,
+    private ragService: RagService,
+    private i18nService: I18nService,
+    private userSettingService: UserSettingService,
+  ) {
+    this.defaultDimensions = parseInt(
+      this.configService.get<string>('DEFAULT_VECTOR_DIMENSIONS', String(DEFAULT_VECTOR_DIMENSIONS)),
+    );
+  }
+
+  async *streamChat(
+    message: string,
+    history: ChatMessage[],
+    userId: string,
+    modelConfig: ModelConfig,
+    userLanguage: string = DEFAULT_LANGUAGE,
+    selectedEmbeddingId?: string,
+    selectedGroups?: string[], // 新規:選択されたグループ
+    selectedFiles?: string[], // 新規:選択されたファイル
+    historyId?: string, // 新規:対話履歴ID
+    enableRerank: boolean = false,
+    selectedRerankId?: string,
+    temperature?: number, // 新規: temperature パラメータ
+    maxTokens?: number, // 新規: maxTokens パラメータ
+    topK?: number, // 新規: topK パラメータ
+    similarityThreshold?: number, // 新規: similarityThreshold パラメータ
+    enableQueryExpansion: boolean = false,
+    enableHyDE: boolean = false,
+    scoreThreshold?: number,
+  ): AsyncGenerator<{ type: 'content' | 'sources'; data: any }> {
+    console.log('=== ChatService.streamChat ===');
+    console.log('ユーザーID:', userId);
+    console.log('ユーザー言語:', userLanguage);
+    console.log('選択された埋め込みモデルID:', selectedEmbeddingId);
+    console.log('選択されたグループ:', selectedGroups);
+    console.log('選択されたファイル:', selectedFiles);
+    console.log('履歴ID:', historyId);
+    console.log('Temperature:', temperature);
+    console.log('Max Tokens:', maxTokens);
+    console.log('Top K:', topK);
+    console.log('類似度しきい値:', similarityThreshold);
+    console.log('Query Expansion:', enableQueryExpansion);
+    console.log('HyDE:', enableHyDE);
+    console.log('モデル設定:', {
+      name: modelConfig.name,
+      modelId: modelConfig.modelId,
+      baseUrl: modelConfig.baseUrl,
+    });
+    console.log('API Key プレフィックス:', modelConfig.apiKey?.substring(0, 10) + '...');
+    console.log('API Key 長さ:', modelConfig.apiKey?.length);
+
+    // 現在の言語設定を取得 (下位互換性のためにLANGUAGE_CONFIGを保持しますが、現在はi18nサービスを使>用)
+    // ユーザー設定に基づいて実際の言語を使用
+    const effectiveUserLanguage = userLanguage || DEFAULT_LANGUAGE;
+
+    let currentHistoryId = historyId;
+    let fullResponse = '';
+
+    try {
+      // historyId がない場合は、新しい対話履歴を作成
+      if (!currentHistoryId) {
+        const searchHistory = await this.searchHistoryService.create(
+          userId,
+          message,
+          selectedGroups,
+        );
+        currentHistoryId = searchHistory.id;
+        console.log(this.i18nService.getMessage('creatingHistory', effectiveUserLanguage) + currentHistoryId);
+      }
+
+      // ユーザーメッセージを保存
+      await this.searchHistoryService.addMessage(currentHistoryId, 'user', message);
+      // 1. ユーザーの埋め込みモデル設定を取得
+      const models = await this.modelConfigService.findAll(userId);
+
+      // ユーザーが選択した埋め込みモデルIDを優先し、そうでない場合は最初のものを使用
+      let embeddingModel;
+      if (selectedEmbeddingId) {
+        embeddingModel = models.find(
+          (m) => m.id === selectedEmbeddingId && m.type === 'embedding' && m.apiKey && m.isEnabled !== false,
+        );
+        console.log(this.i18nService.getMessage('searchingModelById', effectiveUserLanguage) + selectedEmbeddingId);
+      }
+
+      // 見つからない場合は、デフォルトの埋め込みモデルに戻る
+      if (!embeddingModel) {
+        console.log('デフォルトの埋め込みモデルを検索中...');
+        embeddingModel = models.find(
+          (m) => m.type === 'embedding' && m.apiKey && m.isDefault && m.isEnabled !== false,
+        );
+      }
+
+      // それでも見つからない場合は、最初に使用可能な埋め込みモデルに戻る(後方互換性)
+      if (!embeddingModel) {
+        console.log(this.i18nService.getMessage('searchModelFallback', effectiveUserLanguage));
+        embeddingModel = models.find(
+          (m) => m.type === 'embedding' && m.apiKey && m.isEnabled !== false,
+        );
+      }
+
+      if (!embeddingModel) {
+        console.log(this.i18nService.getMessage('noEmbeddingModelFound', effectiveUserLanguage));
+        yield { type: 'content', data: this.i18nService.getMessage('noEmbeddingModel', effectiveUserLanguage) };
+        return;
+      }
+
+      console.log(this.i18nService.getMessage('usingEmbeddingModel', effectiveUserLanguage) + embeddingModel.name + ' ' + embeddingModel.modelId + ' ID:' + embeddingModel.id);
+
+      // 2. ユーザーのクエリを直接使用して検索
+      console.log(this.i18nService.getMessage('startingSearch', effectiveUserLanguage));
+      yield { type: 'content', data: this.i18nService.getMessage('searching', effectiveUserLanguage) + '\n' };
+
+      let searchResults: any[] = [];
+      let context = '';
+
+      try {
+        // 3. 選択された知識グループがある場合、まずそれらのグループ内のファイルIDを取得
+        let effectiveFileIds = selectedFiles; // 明示的に指定されたファイルを優先
+        if (!effectiveFileIds && selectedGroups && selectedGroups.length > 0) {
+          // ナレッジグループからファイルIDを取得
+          effectiveFileIds = await this.knowledgeGroupService.getFileIdsByGroups(selectedGroups, userId);
+          // 如果组内没有文件,应该是一个空数组,从而在后面限制检索范围为“无”
+          if (!effectiveFileIds) {
+            effectiveFileIds = [];
+          }
+        }
+
+        // 3. RagService を使用して検索 (混合検索 + Rerank をサポート)
+        const ragResults = await this.ragService.searchKnowledge(
+          message,
+          userId,
+          topK !== undefined ? topK : 5, // 渡されたtopK値を使用、デフォルトは5
+          similarityThreshold !== undefined ? similarityThreshold : 0.3, // 渡されたsimilarityThreshold値を使用、デフォルトは0.3
+          embeddingModel.id,
+          true, // enableFullTextSearch (Chat defaults to hybrid)
+          enableRerank,
+          selectedRerankId,
+          undefined, // selectedGroups - 現在はeffectiveFileIdsパラメータを通じて渡されます
+          effectiveFileIds,  // effectiveFileIds (selectedGroupsから取得したファイルIDを含む)
+          enableQueryExpansion,
+          enableHyDE,
+          scoreThreshold !== undefined ? scoreThreshold : 0.5,
+        );
+
+        // RagSearchResult を ChatService が必要とする形式 (any[]) に変換
+        // HybridSearch は ES の hit 構造を返しますが、RagSearchResult は正規化されています。
+        // BuildContext は {fileName, content} を期待します。RagSearchResult はこれらを持っています。
+        searchResults = ragResults;
+        console.log(this.i18nService.getMessage('searchResultsCount', effectiveUserLanguage) + searchResults.length);
+
+        // 4. コンテキストの構築
+        context = this.buildContext(searchResults);
+
+        if (searchResults.length === 0) {
+          if (selectedGroups && selectedGroups.length > 0) {
+            // ユーザーがナレッジグループを選択したが、一致するものが見つからなかった場合
+            const noMatchMsg = this.i18nService.getMessage('noMatchInKnowledgeGroup', effectiveUserLanguage);
+            yield { type: 'content', data: `⚠️ ${noMatchMsg}\n\n` };
+          } else {
+            yield { type: 'content', data: this.i18nService.getMessage('noResults', effectiveUserLanguage) + '\n\n' };
+          }
+          yield { type: 'content', data: `[Debug] ${this.i18nService.getMessage('searchScope', effectiveUserLanguage)}: ${selectedFiles ? selectedFiles.length + ' ' + this.i18nService.getMessage('files', effectiveUserLanguage) : selectedGroups ? selectedGroups.length + ' ' + this.i18nService.getMessage('notebooks', effectiveUserLanguage) : this.i18nService.getMessage('all', effectiveUserLanguage)}\n` };
+          yield { type: 'content', data: `[Debug] ${this.i18nService.getMessage('searchResults', effectiveUserLanguage)}: 0 ${this.i18nService.getMessage('items', effectiveUserLanguage)}\n` };
+        } else {
+          yield {
+            type: 'content',
+            data: `${searchResults.length} ${this.i18nService.getMessage('relevantInfoFound', effectiveUserLanguage)}。${this.i18nService.getMessage('generatingResponse', effectiveUserLanguage)}...\n\n`,
+          };
+          // 一時的なデバッグ情報
+          const scores = searchResults.map(r => {
+            if (r.originalScore !== undefined && r.originalScore !== r.score) {
+              return `${r.originalScore.toFixed(2)} → ${r.score.toFixed(2)}`;
+            }
+            return r.score.toFixed(2);
+          }).join(', ');
+          const files = [...new Set(searchResults.map(r => r.fileName))].join(', ');
+          yield { type: 'content', data: `> [Debug] ${this.i18nService.getMessage('searchHits', effectiveUserLanguage)}: ${searchResults.length} ${this.i18nService.getMessage('items', effectiveUserLanguage)}\n` };
+          yield { type: 'content', data: `> [Debug] ${this.i18nService.getMessage('relevance', effectiveUserLanguage)}: ${scores}\n` };
+          yield { type: 'content', data: `> [Debug] ${this.i18nService.getMessage('sourceFiles', effectiveUserLanguage)}: ${files}\n\n---\n\n` };
+        }
+      } catch (searchError) {
+        console.error(this.i18nService.getMessage('searchFailedLog', effectiveUserLanguage) + ':', searchError);
+        yield { type: 'content', data: this.i18nService.getMessage('searchFailed', effectiveUserLanguage) + '\n\n' };
+      }
+
+      // 5. ストリーム回答生成
+      this.logger.log(`${this.i18nService.getMessage('modelCall', effectiveUserLanguage)} タイプ: LLM, モデル: ${modelConfig.name} (${modelConfig.modelId}), ユーザー: ${userId}`);
+      const llm = new ChatOpenAI({
+        apiKey: modelConfig.apiKey,
+        streaming: true,
+        temperature: temperature !== undefined ? temperature : 0.3,
+        maxTokens: maxTokens !== undefined ? maxTokens : undefined,
+        modelName: modelConfig.modelId,
+        configuration: {
+          baseURL: modelConfig.baseUrl || 'https://api.openai.com/v1',
+        },
+      });
+
+      const promptTemplate =
+        context.length > 0
+          ? this.i18nService.getPrompt(
+            effectiveUserLanguage,
+            'withContext',
+            selectedGroups && selectedGroups.length > 0
+          )
+          : this.i18nService.getPrompt(effectiveUserLanguage, 'withoutContext');
+
+      const prompt = PromptTemplate.fromTemplate(promptTemplate);
+
+      const chain = prompt.pipe(llm);
+
+      const stream = await chain.stream({
+        context,
+        history: this.formatHistory(history, userLanguage),
+        question: message,
+      });
+
+      for await (const chunk of stream) {
+        if (chunk.content) {
+          fullResponse += chunk.content;
+          yield { type: 'content', data: chunk.content };
+        }
+      }
+
+      // AI 回答を保存
+      await this.searchHistoryService.addMessage(
+        currentHistoryId,
+        'assistant',
+        fullResponse,
+        searchResults.map((result) => ({
+          fileName: result.fileName,
+          content: String(result.content).substring(0, 200) + '...',
+          score: result.score,
+          chunkIndex: result.chunkIndex,
+          pageNumber: result.pageNumber, // 追加
+          fileId: result.fileId,
+        })),
+      );
+
+      // 初期タイトルの更新(最初のメッセージペアの後に非同期で実行)
+      // タイトルが非常に短い場合やデフォルトの場合にのみ更新するロジックを検討
+      const messages = await this.searchHistoryService.findOne(currentHistoryId, userId);
+      if (messages.messages.length === 2) {
+        this.generateChatTitle(currentHistoryId, message, fullResponse, userId).catch(err => {
+          this.logger.error(`Failed to generate title for ${currentHistoryId}`, err);
+        });
+      }
+
+      // 6. 引用元を返却
+      yield {
+        type: 'sources',
+        data: searchResults.map((result) => ({
+          fileName: result.fileName,
+          content: String(result.content).substring(0, 200) + '...',
+          score: result.score,
+          chunkIndex: result.chunkIndex,
+          fileId: result.fileId,
+        })),
+      };
+    } catch (error) {
+      this.logger.error(this.i18nService.getMessage('chatStreamError', effectiveUserLanguage), error);
+      yield { type: 'content', data: `${this.i18nService.getMessage('error', effectiveUserLanguage)}: ${error.message}` };
+    }
+  }
+
+  async *streamAssist(
+    instruction: string,
+    context: string,
+    modelConfig: ModelConfig,
+  ): AsyncGenerator<{ type: 'content'; data: any }> {
+    try {
+      this.logger.log(`${this.i18nService.getMessage('modelCall', 'ja')} タイプ: LLM (Assist), モデル: ${modelConfig.name} (${modelConfig.modelId})`);
+      const llm = new ChatOpenAI({
+        apiKey: modelConfig.apiKey,
+        streaming: true,
+        temperature: 0.7,
+        modelName: modelConfig.modelId,
+        configuration: {
+          baseURL: modelConfig.baseUrl || 'https://api.openai.com/v1',
+        },
+      });
+
+      const systemPrompt = `${this.i18nService.getMessage('intelligentAssistant', 'ja')}
+提供されたテキスト内容を、ユーザーの指示に基づいて修正または改善してください。
+挨拶や結びの言葉(「わかりました、こちらが...」など)は含めず、修正後の内容のみを直接出力してください。
+
+コンテキスト(現在の内容):
+${context}
+
+ユーザーの指示:
+${instruction}`;
+
+      const stream = await llm.stream(systemPrompt);
+
+      for await (const chunk of stream) {
+        if (chunk.content) {
+          yield { type: 'content', data: chunk.content };
+        }
+      }
+    } catch (error) {
+      this.logger.error(this.i18nService.getMessage('assistStreamError', 'ja'), error);
+      yield { type: 'content', data: `${this.i18nService.getMessage('error', 'ja')}: ${error.message}` };
+    }
+  }
+
+  private async hybridSearch(
+    keywords: string[],
+    userId: string,
+    embeddingModelId?: string,
+    selectedGroups?: string[], // 新規パラメータ
+    explicitFileIds?: string[], // 新規パラメータ
+  ): Promise<any[]> {
+    try {
+      // キーワードを検索文字列に結合
+      const combinedQuery = keywords.join(' ');
+      console.log(this.i18nService.getMessage('searchString', 'ja') + combinedQuery);
+
+      // 埋め込みモデルIDが提供されているか確認
+      if (!embeddingModelId) {
+        console.log(this.i18nService.getMessage('embeddingModelIdNotProvided', 'ja'));
+        return [];
+      }
+
+      // 実際の埋め込みベクトルを使用
+      console.log(this.i18nService.getMessage('generatingEmbeddings', 'ja'));
+      const queryEmbedding = await this.embeddingService.getEmbeddings(
+        [combinedQuery],
+        userId,
+        embeddingModelId,
+      );
+      const queryVector = queryEmbedding[0];
+      console.log(this.i18nService.getMessage('embeddingsGenerated', 'ja') + this.i18nService.getMessage('dimensions', 'ja') + ':', queryVector.length);
+
+      // 混合検索
+      console.log(this.i18nService.getMessage('performingHybridSearch', 'ja'));
+      const results = await this.elasticsearchService.hybridSearch(
+        queryVector,
+        combinedQuery,
+        userId,
+        10,
+        0.6,
+        selectedGroups, // 選択されたグループを渡す
+        explicitFileIds, // 明示的なファイルIDを渡す
+      );
+      console.log(this.i18nService.getMessage('esSearchCompleted', 'ja') + this.i18nService.getMessage('resultsCount', 'ja') + ':', results.length);
+
+      return results.slice(0, 10);
+    } catch (error) {
+      console.error(this.i18nService.getMessage('hybridSearchFailed', 'ja') + ':', error);
+      return [];
+    }
+  }
+
+  private buildContext(results: any[]): string {
+    return results
+      .map(
+        (result, index) =>
+          `[${index + 1}] ${this.i18nService.getMessage('file', 'ja')}:${result.fileName}\n${this.i18nService.getMessage('content', 'ja')}:${result.content}\n`,
+      )
+      .join('\n');
+  }
+
+  private formatHistory(
+    history: ChatMessage[],
+    userLanguage: string = 'ja',
+  ): string {
+    const userLabel = this.i18nService.getMessage('userLabel', userLanguage);
+    const assistantLabel = this.i18nService.getMessage('assistantLabel', userLanguage);
+
+    return history
+      .slice(-6)
+      .map(
+        (msg) =>
+          `${msg.role === 'user' ? userLabel : assistantLabel}:${msg.content}`,
+      )
+      .join('\n');
+  }
+  async getContextForTopic(topic: string, userId: string, groupId?: string, fileIds?: string[]): Promise<string> {
+    try {
+      const models = await this.modelConfigService.findAll(userId);
+
+      // デフォルトの埋め込みモデルを優先
+      let embeddingModel = models.find(m => m.type === 'embedding' && m.apiKey && m.isDefault && m.isEnabled !== false);
+
+      // デフォルトがない場合、有効な最初のモデルを使用
+      if (!embeddingModel) {
+        embeddingModel = models.find(m => m.type === 'embedding' && m.apiKey && m.isEnabled !== false);
+      }
+      if (!embeddingModel) return '';
+
+      const results = await this.hybridSearch(
+        [topic],
+        userId,
+        embeddingModel.id,
+        groupId ? [groupId] : undefined,
+        fileIds
+      );
+
+      return this.buildContext(results);
+    } catch (err) {
+      this.logger.error(`${this.i18nService.getMessage('getContextForTopicFailed', 'ja')}: ${err.message}`);
+      return '';
+    }
+  }
+
+  async generateSimpleChat(
+    messages: ChatMessage[],
+    userId: string,
+    modelConfig?: ModelConfig, // Optional, looks up if not provided
+    temperature: number = 0.7, // Default to 0.7
+  ): Promise<string> {
+    try {
+      let config = modelConfig;
+      if (!config) {
+        // Find default LLM
+        const models = await this.modelConfigService.findAll(userId);
+        // Cast to unknown first to bypass partial mismatch between Entity and Interface
+        // デフォルトのLLMモデルを優先
+        let found = models.find(m => m.type === 'llm' && m.isDefault && m.isEnabled !== false);
+
+        // デフォルトがない場合、有効な最初のモデルを使用
+        if (!found) {
+          found = models.find(m => m.type === 'llm' && m.isEnabled !== false);
+        }
+        if (found) {
+          config = found as unknown as ModelConfig;
+        }
+
+        if (!config) {
+          throw new Error(this.i18nService.getMessage('noLLMConfigured', 'ja'));
+        }
+      }
+
+      this.logger.log(`${this.i18nService.getMessage('modelCall', 'ja')} タイプ: LLM (Simple), モデル: ${config.name} (${config.modelId}), ユーザー: ${userId}`);
+      const llm = new ChatOpenAI({
+        apiKey: config.apiKey,
+        temperature: temperature,
+        modelName: config.modelId,
+        configuration: {
+          baseURL: config.baseUrl || 'https://api.openai.com/v1',
+        },
+      });
+
+      const response = await llm.invoke(
+        messages.map(m => [m.role, m.content])
+      );
+
+      return String(response.content);
+    } catch (error) {
+      this.logger.error(this.i18nService.getMessage('simpleChatGenerationError', 'ja'), error);
+      throw error;
+    }
+  }
+
+  async generateChatTitle(historyId: string, userMessage: string, assistantResponse: string, userId: string): Promise<string | null> {
+    try {
+      const userLanguage = await this.userSettingService.getLanguage(userId);
+
+      const languageMap: Record<string, string> = {
+        'ja': 'Japanese',
+        'en': 'English',
+        'zh': 'Simplified Chinese',
+      };
+      const targetLanguage = languageMap[userLanguage] || userLanguage;
+
+      const prompt = `Based on the following conversation snippet, generate a short, descriptive title (max 50 chars) that summarizes the topic.
+DO NOT include prefixes like "Title:" or quotes.
+Return ONLY the title text.
+IMPORTANT: Generate the title in ${targetLanguage}.
+Snippet:
+User: ${userMessage}
+AI: ${assistantResponse}`;
+
+      const title = await this.generateSimpleChat(
+        [{ role: 'user', content: prompt }],
+        userId,
+        undefined,
+        0.3, // Use lower temperature for deterministic output
+      );
+
+      // Enhanced cleaning
+      let cleanTitle = title.trim()
+        .replace(/^["']|["']$/g, '') // Remove quotes
+        .replace(/^(Title|Subject|Topic):\s*/i, ''); // Remove common prefixes
+
+      if (cleanTitle.length > 50) {
+        cleanTitle = cleanTitle.substring(0, 50);
+      }
+
+      await this.searchHistoryService.updateTitle(historyId, cleanTitle);
+      this.logger.log(`Generated title for chat ${historyId}: ${cleanTitle}`);
+      return cleanTitle;
+    } catch (err) {
+      this.logger.error(`Failed to generate chat title for ${historyId}`, err);
+      return null;
+    }
+  }
+}

+ 739 - 0
server/src/chat/chat.service.updated.ts

@@ -0,0 +1,739 @@
+import { Injectable, Logger, Inject, forwardRef } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { ChatOpenAI } from '@langchain/openai';
+import { PromptTemplate } from '@langchain/core/prompts';
+import { ElasticsearchService } from '../elasticsearch/elasticsearch.service';
+import { EmbeddingService } from '../knowledge-base/embedding.service';
+import { ModelConfigService } from '../model-config/model-config.service';
+import { KnowledgeGroupService } from '../knowledge-group/knowledge-group.service';
+import { SearchHistoryService } from '../search-history/search-history.service';
+import { ModelConfig } from '../types';
+import { RagService } from '../rag/rag.service';
+
+// 国际化消息管理
+const I18N_MESSAGES = {
+  ja: {
+    noEmbeddingModel: '先にシステム設定で埋め込みモデルを設定してください',
+    searching: 'ナレッジベースを検索中...',
+    noResults: '関連する知識が見つかりませんでした。一般的な知識に基づいて回答します...',
+    searchFailed: 'ナレッジベース検索に失敗しました。一般的な知識に基づいて回答します...',
+    generatingResponse: '回答を生成中',
+    files: '個のファイル',
+    notebooks: '個のノートブック',
+    all: 'すべて',
+    items: '件',
+    searchResults: '検索結果',
+    relevantInfoFound: '件の関連情報が見つかりました',
+    searchHits: '検索ヒット',
+    relevance: '関連度',
+    sourceFiles: '元ファイル',
+    searchScope: '検索範囲',
+    error: 'エラー',
+    creatingHistory: '新規対話履歴を作成:',
+    searchingModelById: 'selectedEmbeddingId に基づいてモデルを検索:',
+    searchModelFallback: '指定された埋め込みモデルが見つかりません。最初に使用可能なモデルを使用します。',
+    noEmbeddingModelFound: '埋め込みモデルの設定が見つかりません',
+    usingEmbeddingModel: '使用する埋め込みモデル:',
+    startingSearch: 'ナレッジベースの検索を開始...',
+    searchResultsCount: '検索結果数:',
+    searchFailedLog: '検索失敗',
+    modelCall: '[モデル呼び出し]',
+    chatStreamError: 'Chat stream error',
+    assistStreamError: 'Assist stream error',
+    file: 'ファイル',
+    content: '内容',
+    userLabel: 'ユーザー',
+    assistantLabel: 'アシスタント',
+    intelligentAssistant: 'あなたはインテリジェントな執筆アシスタントです。',
+    searchString: '検索文字列:',
+    embeddingModelIdNotProvided: '埋め込みモデルIDが提供されていません',
+    generatingEmbeddings: '埋め込みベクトルを生成中...',
+    embeddingsGenerated: '埋め込みベクトルの生成が完了しました',
+    dimensions: '次元数',
+    performingHybridSearch: 'ES 混合検索を実行中...',
+    esSearchCompleted: 'ES 検索が完了しました',
+    resultsCount: '結果数',
+    hybridSearchFailed: '混合検索に失敗しました',
+    getContextForTopicFailed: 'getContextForTopic failed',
+    noLLMConfigured: 'No LLM model configured for user',
+    simpleChatGenerationError: 'Simple chat generation error',
+  },
+  zh: {
+    noEmbeddingModel: '先にシステム設定で埋め込みモデルを設定してください', // 根据要求应使用日语
+    searching: 'ナレッジベースを検索中...', // 根据要求应使用日语
+    noResults: '関連する知識が見つかりませんでした。一般的な知識に基づいて回答します...', // 根据要求应使用日语
+    searchFailed: 'ナレッジベース検索に失敗しました。一般的な知識に基づいて回答します...', // 根据要求应使用日语
+    generatingResponse: '回答を生成中',
+    files: '个文件',
+    notebooks: '个笔记本',
+    all: '全部',
+    items: '个',
+    searchResults: '搜索结果',
+    relevantInfoFound: '条相关信息找到',
+    searchHits: '搜索命中',
+    relevance: '相关度',
+    sourceFiles: '源文件',
+    searchScope: '搜索范围',
+    error: '错误',
+    creatingHistory: '创建新对话历史:',
+    searchingModelById: '根据ID搜索模型:',
+    searchModelFallback: '未找到指定的嵌入模型,使用第一个可用模型。',
+    noEmbeddingModelFound: '找不到嵌入模型设置',
+    usingEmbeddingModel: '使用的嵌入模型:',
+    startingSearch: '开始搜索知识库...',
+    searchResultsCount: '搜索结果数:',
+    searchFailedLog: '搜索失败',
+    modelCall: '[模型调用]',
+    chatStreamError: '聊天流错误',
+    assistStreamError: '辅助流错误',
+    file: '文件',
+    content: '内容',
+    userLabel: '用户',
+    assistantLabel: '助手',
+    intelligentAssistant: '您是智能写作助手。',
+    searchString: '搜索字符串:',
+    embeddingModelIdNotProvided: '未提供嵌入模型ID',
+    generatingEmbeddings: '生成嵌入向量...',
+    embeddingsGenerated: '嵌入向量生成完成',
+    dimensions: '维度',
+    performingHybridSearch: '执行混合搜索...',
+    esSearchCompleted: 'ES搜索完成',
+    resultsCount: '结果数',
+    hybridSearchFailed: '混合搜索失败',
+    getContextForTopicFailed: '获取主题上下文失败',
+    noLLMConfigured: '用户未配置LLM模型',
+    simpleChatGenerationError: '简单聊天生成错误',
+  },
+  en: {
+    noEmbeddingModel: 'Please configure embedding model in system settings first',
+    searching: 'Searching knowledge base...',
+    noResults: 'No relevant knowledge found, will answer based on general knowledge...',
+    searchFailed: 'Knowledge base search failed, will answer based on general knowledge...',
+    generatingResponse: 'Generating response',
+    files: 'files',
+    notebooks: 'notebooks',
+    all: 'all',
+    items: '',
+    searchResults: 'Search results',
+    relevantInfoFound: 'relevant info found',
+    searchHits: 'search hits',
+    relevance: 'relevance',
+    sourceFiles: 'source files',
+    searchScope: 'search scope',
+    error: 'Error',
+    creatingHistory: 'Creating new chat history:',
+    searchingModelById: 'Searching model by ID:',
+    searchModelFallback: 'Specified embedding model not found. Using first available model.',
+    noEmbeddingModelFound: 'No embedding model settings found',
+    usingEmbeddingModel: 'Using embedding model:',
+    startingSearch: 'Starting knowledge base search...',
+    searchResultsCount: 'Search results count:',
+    searchFailedLog: 'Search failed',
+    modelCall: '[Model call]',
+    chatStreamError: 'Chat stream error',
+    assistStreamError: 'Assist stream error',
+    file: 'File',
+    content: 'Content',
+    userLabel: 'User',
+    assistantLabel: 'Assistant',
+    intelligentAssistant: 'You are an intelligent writing assistant.',
+    searchString: 'Search string:',
+    embeddingModelIdNotProvided: 'Embedding model ID not provided',
+    generatingEmbeddings: 'Generating embeddings...',
+    embeddingsGenerated: 'Embeddings generated successfully',
+    dimensions: 'dimensions',
+    performingHybridSearch: 'Performing hybrid search...',
+    esSearchCompleted: 'ES search completed',
+    resultsCount: 'Results count',
+    hybridSearchFailed: 'Hybrid search failed',
+    getContextForTopicFailed: 'getContextForTopic failed',
+    noLLMConfigured: 'No LLM model configured for user',
+    simpleChatGenerationError: 'Simple chat generation error',
+  }
+};
+
+// 简化的国际化服务
+class SimpleI18nService {
+  static getMessage(key: string, language: string = 'ja'): string {
+    // 如果指定语言不存在,则回退到日语
+    const lang = I18N_MESSAGES[language] ? language : 'ja';
+    return I18N_MESSAGES[lang][key] || key;
+  }
+
+  static getPrompt(lang: string = 'ja', type: 'withContext' | 'withoutContext' = 'withContext'): string {
+    if (lang === 'zh') {
+      return type === 'withContext' ? `
+基于以下知识库内容回答用户问题。
+
+知识库内容:
+{context}
+
+历史对话:
+{history}
+
+用户问题:{question}
+
+请用中文回答,并严格遵循以下 Markdown 格式要求:
+
+1. **段落与结构**:
+   - 使用清晰的段落分隔,每个要点之间空一行
+   - 使用标题(## 或 ###)组织长回答
+
+2. **文本格式**:
+   - 使用 **粗体** 强调重要概念和关键词
+   - 使用列表(- 或 1.)组织多个要点
+   - 使用 \`代码\` 标记技术术语、命令、文件名
+
+3. **代码展示**:
+   - 使用代码块展示代码,并指定语言:
+     \`\`\`python
+     def example():
+         return "示例"
+     \`\`\`
+   - 支持的语言:python, javascript, typescript, java, bash, sql 等
+
+4. **流程图与图表**:
+   - 使用 Mermaid 语法展示流程图、时序图等:
+     \`\`\`mermaid
+     graph LR
+         A[开始] --> B[处理]
+         B --> C[结束]
+     \`\`\`
+   - 适用场景:流程说明、架构图、状态图、时序图
+
+5. **其他要求**:
+   - 回答要简洁明了,避免冗长
+   - 如果有多个步骤,使用编号列表
+   - 使用表格展示对比信息(如果适用)
+` : `
+作为一个智能助手,请回答用户的问题。
+
+历史对话:
+{history}
+
+用户问题:{question}
+
+请用中文回答。
+`;
+    } else if (lang === 'en') {
+      return type === 'withContext' ? `
+Answer the user's question based on the following knowledge base content.
+
+Knowledge base content:
+{context}
+
+Conversation history:
+{history}
+
+User question: {question}
+
+Please answer in English and strictly follow these Markdown formatting guidelines:
+
+1. **Paragraphs & Structure**:
+   - Use clear paragraph breaks with blank lines between key points
+   - Use headings (## or ###) to organize longer answers
+
+2. **Text Formatting**:
+   - Use **bold** to emphasize important concepts and keywords
+   - Use lists (- or 1.) to organize multiple points
+   - Use \`code\` to mark technical terms, commands, file names
+
+3. **Code Display**:
+   - Use code blocks with language specification:
+     \`\`\`python
+     def example():
+         return "example"
+     \`\`\`
+   - Supported languages: python, javascript, typescript, java, bash, sql, etc.
+
+4. **Diagrams & Charts**:
+   - Use Mermaid syntax for flowcharts, sequence diagrams, etc.:
+     \`\`\`mermaid
+     graph LR
+         A[Start] --> B[Process]
+         B --> C[End]
+     \`\`\`
+   - Use cases: process flows, architecture diagrams, state diagrams, sequence diagrams
+
+5. **Other Requirements**:
+   - Keep answers concise and clear
+   - Use numbered lists for multi-step processes
+   - Use tables for comparison information (if applicable)
+` : `
+As an intelligent assistant, please answer the user's question.
+
+Conversation history:
+{history}
+
+User question: {question}
+
+Please answer in English.
+`;
+    } else { // 默认为日语,符合项目要求
+      return type === 'withContext' ? `
+以下のナレッジベースの内容に基づいてユーザーの質問に答えてください。
+
+ナレッジベースの内容:
+{context}
+
+会話履歴:
+{history}
+
+ユーザーの質問:{question}
+
+日本語で回答してください。以下の Markdown 書式要件に厳密に従ってください:
+
+1. **段落と構造**:
+   - 明確な段落分けを使用し、要点間に空行を入れる
+   - 長い回答には見出し(## または ###)を使用
+
+2. **テキスト書式**:
+   - 重要な概念やキーワードを強調するために **太字** を使用
+   - 複数のポイントを整理するためにリスト(- または 1.)を使用
+   - 技術用語、コマンド、ファイル名をマークするために \`コード\` を使用
+
+3. **コード表示**:
+   - 言語を指定してコードブロックを使用:
+     \`\`\`python
+     def example():
+         return "例"
+     \`\`\`
+   - 対応言語:python, javascript, typescript, java, bash, sql など
+
+4. **図表とチャート**:
+   - フローチャート、シーケンス図などに Mermaid 構文を使用:
+     \`\`\`mermaid
+     graph LR
+         A[開始] --> B[処理]
+         B --> C[終了]
+     \`\`\`
+   - 使用例:プロセスフロー、アーキテクチャ図、状態図、シーケンス図
+
+5. **その他の要件**:
+   - 簡潔で明確な回答を心がける
+   - 複数のステップがある場合は番号付きリストを使用
+   - 比較情報には表を使用(該当する場合)
+` : `
+インテリジェントアシスタントとして、ユーザーの質問に答えてください。
+
+会話履歴:
+{history}
+
+ユーザーの質問:{question}
+
+日本語で回答してください。
+`;
+    }
+  }
+}
+
+export interface ChatMessage {
+  role: 'user' | 'assistant';
+  content: string;
+}
+
+@Injectable()
+export class ChatService {
+  private readonly logger = new Logger(ChatService.name);
+  private readonly defaultDimensions: number;
+
+  constructor(
+    @Inject(forwardRef(() => ElasticsearchService))
+    private elasticsearchService: ElasticsearchService,
+    private embeddingService: EmbeddingService,
+    private modelConfigService: ModelConfigService,
+    @Inject(forwardRef(() => KnowledgeGroupService))
+    private knowledgeGroupService: KnowledgeGroupService,
+    private searchHistoryService: SearchHistoryService,
+    private configService: ConfigService,
+    private ragService: RagService,
+  ) {
+    this.defaultDimensions = parseInt(
+      this.configService.get<string>('DEFAULT_VECTOR_DIMENSIONS', '2560'),
+    );
+  }
+
+  async *streamChat(
+    message: string,
+    history: ChatMessage[],
+    userId: string,
+    modelConfig: ModelConfig,
+    userLanguage: string = 'ja', // 根据项目要求,默认使用日语
+    selectedEmbeddingId?: string,
+    selectedGroups?: string[], // 新規:選択されたグループ
+    selectedFiles?: string[], // 新規:選択されたファイル
+    historyId?: string, // 新規:対話履歴ID
+    enableRerank: boolean = false,
+    selectedRerankId?: string,
+    temperature?: number, // 新增:temperature 参数
+    maxTokens?: number, // 新增:maxTokens 参数
+    topK?: number, // 新增:topK 参数
+    similarityThreshold?: number // 新增:similarityThreshold 参数
+  ): AsyncGenerator<{ type: 'content' | 'sources'; data: any }> {
+    console.log('=== ChatService.streamChat ===');
+    console.log('User ID:', userId);
+    console.log('User Language:', userLanguage);
+    console.log('Selected Embedding ID:', selectedEmbeddingId);
+    console.log('Selected Groups:', selectedGroups);
+    console.log('Selected Files:', selectedFiles);
+    console.log('History ID:', historyId);
+    console.log('Temperature:', temperature);
+    console.log('Max Tokens:', maxTokens);
+    console.log('Top K:', topK);
+    console.log('Similarity Threshold:', similarityThreshold);
+    console.log('Model Config:', {
+      name: modelConfig.name,
+      modelId: modelConfig.modelId,
+      baseUrl: modelConfig.baseUrl,
+    });
+    console.log('API Key プレフィックス:', modelConfig.apiKey?.substring(0, 10) + '...');
+    console.log('API Key 長さ:', modelConfig.apiKey?.length);
+
+    let currentHistoryId = historyId;
+    let fullResponse = '';
+
+    try {
+      // historyId がない場合は、新しい対話履歴を作成
+      if (!currentHistoryId) {
+        const searchHistory = await this.searchHistoryService.create(
+          userId,
+          message,
+          selectedGroups,
+        );
+        currentHistoryId = searchHistory.id;
+        console.log(SimpleI18nService.getMessage('creatingHistory', userLanguage) + currentHistoryId);
+      }
+
+      // ユーザーメッセージを保存
+      await this.searchHistoryService.addMessage(currentHistoryId, 'user', message);
+
+      // 1. ユーザーの埋め込みモデル設定を取得
+      const models = await this.modelConfigService.findAll(userId);
+
+      // ユーザーが選択した埋め込みモデルIDを優先し、そうでない場合は最初のものを使用
+      let embeddingModel;
+      if (selectedEmbeddingId) {
+        embeddingModel = models.find(
+          (m) => m.id === selectedEmbeddingId && m.type === 'embedding' && m.apiKey && m.isEnabled !== false,
+        );
+        console.log(SimpleI18nService.getMessage('searchingModelById', userLanguage) + selectedEmbeddingId);
+      }
+
+      // 見つからない場合は、最初に使用可能な埋め込みモデルに戻る
+      if (!embeddingModel) {
+        console.log(SimpleI18nService.getMessage('searchModelFallback', userLanguage));
+        embeddingModel = models.find(
+          (m) => m.type === 'embedding' && m.apiKey && m.isEnabled !== false,
+        );
+      }
+
+      if (!embeddingModel) {
+        console.log(SimpleI18nService.getMessage('noEmbeddingModelFound', userLanguage));
+        yield { type: 'content', data: SimpleI18nService.getMessage('noEmbeddingModel', userLanguage) };
+        return;
+      }
+
+      console.log(SimpleI18nService.getMessage('usingEmbeddingModel', userLanguage) +
+                  embeddingModel.name + ' ' + embeddingModel.modelId + ', ID:' + embeddingModel.id);
+
+      // 2. ユーザーのクエリを直接使用して検索
+      console.log(SimpleI18nService.getMessage('startingSearch', userLanguage));
+      yield { type: 'content', data: SimpleI18nService.getMessage('searching', userLanguage) + '\n' };
+
+      let searchResults: any[] = [];
+      let context = '';
+
+      try {
+        // 3. 選択された知識グループがある場合、まずそれらのグループ内のファイルIDを取得
+        let effectiveFileIds = selectedFiles; // 优先使用明确指定的文件
+        if (!effectiveFileIds && selectedGroups && selectedGroups.length > 0) {
+          // 从知识组获取文件ID
+          effectiveFileIds = await this.knowledgeGroupService.getFileIdsByGroups(selectedGroups, userId);
+        }
+
+        // 3. RagService を使用して検索 (混合検索 + Rerank をサポート)
+        const ragResults = await this.ragService.searchKnowledge(
+          message,
+          userId,
+          topK !== undefined ? topK : 5, // 使用传递的topK值,默认为5
+          similarityThreshold !== undefined ? similarityThreshold : 0.6, // 使用传递的similarityThreshold值,默认为0.6
+          embeddingModel.id,
+          true, // enableFullTextSearch (Chat defaults to hybrid)
+          enableRerank,
+          selectedRerankId,
+          undefined, // selectedGroups - 现在通过effectiveFileIds参数传递
+          effectiveFileIds  // effectiveFileIds (包括从selectedGroups获取的文件ID)
+        );
+
+        // RagSearchResult を ChatService が必要とする形式 (any[]) に変換
+        // HybridSearch は ES の hit 構造を返しますが、RagSearchResult は正規化されています。
+        // BuildContext は {fileName, content} を期待します。RagSearchResult はこれらを持っています。
+        searchResults = ragResults;
+        console.log(SimpleI18nService.getMessage('searchResultsCount', userLanguage) + searchResults.length);
+
+        // 4. コンテキストの構築
+        context = this.buildContext(searchResults);
+
+        if (searchResults.length === 0) {
+          yield { type: 'content', data: SimpleI18nService.getMessage('noResults', userLanguage) + '\n\n' };
+          yield { type: 'content', data: `[Debug] ${SimpleI18nService.getMessage('searchScope', userLanguage)}: ${selectedFiles ? selectedFiles.length + ' ' + SimpleI18nService.getMessage('files', userLanguage) : selectedGroups ? selectedGroups.length + ' ' + SimpleI18nService.getMessage('notebooks', userLanguage) : SimpleI18nService.getMessage('all', userLanguage)}\n` };
+          yield { type: 'content', data: `[Debug] ${SimpleI18nService.getMessage('searchResults', userLanguage)}: 0 ${SimpleI18nService.getMessage('items', userLanguage)}\n` };
+        } else {
+          yield {
+            type: 'content',
+            data: `${searchResults.length} ${SimpleI18nService.getMessage('relevantInfoFound', userLanguage)}。${SimpleI18nService.getMessage('generatingResponse', userLanguage)}...\n\n`,
+          };
+          // 一時的なデバッグ情報
+          const scores = searchResults.map(r => r.score.toFixed(2)).join(', ');
+          const files = [...new Set(searchResults.map(r => r.fileName))].join(', ');
+          yield { type: 'content', data: `> [Debug] ${SimpleI18nService.getMessage('searchHits', userLanguage)}: ${searchResults.length} ${SimpleI18nService.getMessage('items', userLanguage)}\n` };
+          yield { type: 'content', data: `> [Debug] ${SimpleI18nService.getMessage('relevance', userLanguage)}: ${scores}\n` };
+          yield { type: 'content', data: `> [Debug] ${SimpleI18nService.getMessage('sourceFiles', userLanguage)}: ${files}\n\n---\n\n` };
+        }
+      } catch (searchError) {
+        console.error(SimpleI18nService.getMessage('searchFailedLog', userLanguage) + ':', searchError);
+        yield { type: 'content', data: SimpleI18nService.getMessage('searchFailed', userLanguage) + '\n\n' };
+      }
+
+      // 5. ストリーム回答生成
+      this.logger.log(`${SimpleI18nService.getMessage('modelCall', userLanguage)} タイプ: LLM, モデル: ${modelConfig.name} (${modelConfig.modelId}), ユーザー: ${userId}`);
+      const llm = new ChatOpenAI({
+        apiKey: modelConfig.apiKey,
+        streaming: true,
+        temperature: temperature !== undefined ? temperature : 0.3,
+        maxTokens: maxTokens !== undefined ? maxTokens : undefined,
+        modelName: modelConfig.modelId,
+        configuration: {
+          baseURL: modelConfig.baseUrl || 'https://api.openai.com/v1',
+        },
+      });
+
+      const promptTemplate =
+        context.length > 0
+          ? SimpleI18nService.getPrompt(userLanguage, 'withContext')
+          : SimpleI18nService.getPrompt(userLanguage, 'withoutContext');
+
+      const prompt = PromptTemplate.fromTemplate(promptTemplate);
+
+      const chain = prompt.pipe(llm);
+
+      const stream = await chain.stream({
+        context,
+        history: this.formatHistory(history, userLanguage),
+        question: message,
+      });
+
+      for await (const chunk of stream) {
+        if (chunk.content) {
+          fullResponse += chunk.content;
+          yield { type: 'content', data: chunk.content };
+        }
+      }
+
+      // AI 回答を保存
+      await this.searchHistoryService.addMessage(
+        currentHistoryId,
+        'assistant',
+        fullResponse,
+        searchResults.map((result) => ({
+          fileName: result.fileName,
+          content: String(result.content).substring(0, 200) + '...',
+          score: result.score,
+          chunkIndex: result.chunkIndex,
+          fileId: result.fileId,
+        })),
+      );
+
+      // 6. 引用元を返却
+      yield {
+        type: 'sources',
+        data: searchResults.map((result) => ({
+          fileName: result.fileName,
+          content: String(result.content).substring(0, 200) + '...',
+          score: result.score,
+          chunkIndex: result.chunkIndex,
+          fileId: result.fileId,
+        })),
+      };
+    } catch (error) {
+      this.logger.error(SimpleI18nService.getMessage('chatStreamError', userLanguage), error);
+      yield { type: 'content', data: `${SimpleI18nService.getMessage('error', userLanguage)}: ${error.message}` };
+    }
+  }
+
+  async *streamAssist(
+    instruction: string,
+    context: string,
+    modelConfig: ModelConfig,
+  ): AsyncGenerator<{ type: 'content'; data: any }> {
+    try {
+      this.logger.log(`${SimpleI18nService.getMessage('modelCall', 'ja')} タイプ: LLM (Assist), モデル: ${modelConfig.name} (${modelConfig.modelId})`);
+      const llm = new ChatOpenAI({
+        apiKey: modelConfig.apiKey,
+        streaming: true,
+        temperature: 0.7,
+        modelName: modelConfig.modelId,
+        configuration: {
+          baseURL: modelConfig.baseUrl || 'https://api.openai.com/v1',
+        },
+      });
+
+      const systemPrompt = `${SimpleI18nService.getMessage('intelligentAssistant', 'ja')}
+提供されたテキスト内容を、ユーザーの指示に基づいて修正または改善してください。
+挨拶や結びの言葉(「わかりました、こちらが...」など)は含めず、修正後の内容のみを直接出力してください。
+
+コンテキスト(現在の内容):
+${context}
+
+ユーザーの指示:
+${instruction}`;
+
+      const stream = await llm.stream(systemPrompt);
+
+      for await (const chunk of stream) {
+        if (chunk.content) {
+          yield { type: 'content', data: chunk.content };
+        }
+      }
+    } catch (error) {
+      this.logger.error(SimpleI18nService.getMessage('assistStreamError', 'ja'), error);
+      yield { type: 'content', data: `${SimpleI18nService.getMessage('error', 'ja')}: ${error.message}` };
+    }
+  }
+
+  private async hybridSearch(
+    keywords: string[],
+    userId: string,
+    embeddingModelId?: string,
+    selectedGroups?: string[], // 新增参数
+    explicitFileIds?: string[], // 新規パラメータ
+  ): Promise<any[]> {
+    try {
+      // キーワードを検索文字列に結合
+      const combinedQuery = keywords.join(' ');
+      console.log(SimpleI18nService.getMessage('searchString', 'ja') + combinedQuery);
+
+      // 埋め込みモデルIDが提供されているか確認
+      if (!embeddingModelId) {
+        console.log(SimpleI18nService.getMessage('embeddingModelIdNotProvided', 'ja'));
+        return [];
+      }
+
+      // 実際の埋め込みベクトルを使用
+      console.log(SimpleI18nService.getMessage('generatingEmbeddings', 'ja'));
+      const queryEmbedding = await this.embeddingService.getEmbeddings(
+        [combinedQuery],
+        userId,
+        embeddingModelId,
+      );
+      const queryVector = queryEmbedding[0];
+      console.log(`${SimpleI18nService.getMessage('embeddingsGenerated', 'ja')}。${SimpleI18nService.getMessage('dimensions', 'ja')}: ${queryVector.length}`);
+
+      // 混合検索
+      console.log(SimpleI18nService.getMessage('performingHybridSearch', 'ja'));
+      const results = await this.elasticsearchService.hybridSearch(
+        queryVector,
+        combinedQuery,
+        userId,
+        10,
+        0.6,
+        selectedGroups, // 選択されたグループを渡す
+        explicitFileIds, // 明示的なファイルIDを渡す
+      );
+      console.log(`${SimpleI18nService.getMessage('esSearchCompleted', 'ja')}。${SimpleI18nService.getMessage('resultsCount', 'ja')}: ${results.length}`);
+
+      return results.slice(0, 10);
+    } catch (error) {
+      console.error(SimpleI18nService.getMessage('hybridSearchFailed', 'ja') + ':', error);
+      return [];
+    }
+  }
+
+  private buildContext(results: any[]): string {
+    return results
+      .map(
+        (result, index) =>
+          `[${index + 1}] ${SimpleI18nService.getMessage('file', 'ja')}:${result.fileName}\n${SimpleI18nService.getMessage('content', 'ja')}:${result.content}\n`,
+      )
+      .join('\n');
+  }
+
+  private formatHistory(
+    history: ChatMessage[],
+    userLanguage: string = 'ja',
+  ): string {
+    const userLabel = SimpleI18nService.getMessage('userLabel', userLanguage);
+    const assistantLabel = SimpleI18nService.getMessage('assistantLabel', userLanguage);
+
+    return history
+      .slice(-6)
+      .map(
+        (msg) =>
+          `${msg.role === 'user' ? userLabel : assistantLabel}:${msg.content}`,
+      )
+      .join('\n');
+  }
+
+  async getContextForTopic(topic: string, userId: string, groupId?: string, fileIds?: string[]): Promise<string> {
+    try {
+      const models = await this.modelConfigService.findAll(userId);
+      const embeddingModel = models.find(m => m.type === 'embedding' && m.apiKey && m.isEnabled !== false);
+      if (!embeddingModel) return '';
+
+      const results = await this.hybridSearch(
+        [topic],
+        userId,
+        embeddingModel.id,
+        groupId ? [groupId] : undefined,
+        fileIds
+      );
+
+      return this.buildContext(results);
+    } catch (err) {
+      this.logger.error(`${SimpleI18nService.getMessage('getContextForTopicFailed', 'ja')}: ${err.message}`);
+      return '';
+    }
+  }
+
+  async generateSimpleChat(
+    messages: ChatMessage[],
+    userId: string,
+    modelConfig?: ModelConfig, // Optional, looks up if not provided
+  ): Promise<string> {
+    try {
+      let config = modelConfig;
+      if (!config) {
+        // Find default LLM
+        const models = await this.modelConfigService.findAll(userId);
+        // Cast to unknown first to bypass partial mismatch between Entity and Interface
+        const found = models.find(m => m.type === 'llm' && m.isEnabled !== false);
+        if (found) {
+          config = found as unknown as ModelConfig;
+        }
+
+        if (!config) {
+          throw new Error(SimpleI18nService.getMessage('noLLMConfigured', 'ja'));
+        }
+      }
+
+      this.logger.log(`${SimpleI18nService.getMessage('modelCall', 'ja')} タイプ: LLM (Simple), モデル: ${config.name} (${config.modelId}), ユーザー: ${userId}`);
+      const llm = new ChatOpenAI({
+        apiKey: config.apiKey,
+        temperature: 0.7, // 独創的な執筆のために温度を高めに設定
+        modelName: config.modelId,
+        configuration: {
+          baseURL: config.baseUrl || 'https://api.openai.com/v1',
+        },
+      });
+
+      const response = await llm.invoke(
+        messages.map(m => [m.role, m.content])
+      );
+
+      return String(response.content);
+    } catch (error) {
+      this.logger.error(SimpleI18nService.getMessage('simpleChatGenerationError', 'ja'), error);
+      throw error;
+    }
+  }
+}

+ 22 - 0
server/src/common/constants.ts

@@ -0,0 +1,22 @@
+/**
+ * アプリケーション全体で使用される定数定義
+ */
+
+// チャンク設定のデフォルト値
+export const DEFAULT_CHUNK_SIZE = 200;
+export const MIN_CHUNK_SIZE = 50;
+export const MAX_CHUNK_SIZE = 8191;
+export const DEFAULT_CHUNK_OVERLAP = 40;
+export const DEFAULT_MAX_OVERLAP_RATIO = 0.5;
+
+// ベクトル次元のデフォルト値 (OpenAI Standard)
+export const DEFAULT_VECTOR_DIMENSIONS = 1536;
+
+// ファイルサイズの制限 (バイト)
+export const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
+
+// バッチ処理の制限
+export const DEFAULT_MAX_BATCH_SIZE = 2048;
+
+// デフォルト言語
+export const DEFAULT_LANGUAGE = 'ja';

+ 39 - 0
server/src/defaults.ts

@@ -0,0 +1,39 @@
+// server/src/defaults.ts
+import { AppSettings, ModelConfig, ModelType } from './types'; // Import from local types
+
+export const DEFAULT_MODELS: ModelConfig[] = [
+  {
+    id: 'default-openai-chat',
+    name: 'OpenAI Compatible Chat',
+    modelId: 'gpt-3.5-turbo',
+    baseUrl: 'https://api.openai.com/v1',
+    type: ModelType.LLM,
+    supportsVision: false,
+  },
+  {
+    id: 'default-embedding',
+    name: 'OpenAI Compatible Embedding',
+    modelId: 'text-embedding-ada-002',
+    baseUrl: 'https://api.openai.com/v1',
+    type: ModelType.EMBEDDING,
+  },
+];
+
+export const DEFAULT_SETTINGS: AppSettings = {
+  selectedLLMId: 'default-openai-chat',
+  selectedEmbeddingId: 'default-embedding',
+  selectedRerankId: '',
+
+  temperature: 0.3,
+  maxTokens: 8192,
+
+  enableRerank: false,
+  topK: 4,
+  scoreThreshold: 0.5,
+
+  enableFullTextSearch: false,
+  enableQueryExpansion: false,
+  enableHyDE: false,
+
+  language: 'ja',
+};

+ 10 - 0
server/src/elasticsearch/elasticsearch.module.ts

@@ -0,0 +1,10 @@
+import { Module, forwardRef } from '@nestjs/common';
+import { ElasticsearchService } from './elasticsearch.service';
+import { KnowledgeGroupModule } from '../knowledge-group/knowledge-group.module';
+
+@Module({
+  imports: [forwardRef(() => KnowledgeGroupModule)],
+  providers: [ElasticsearchService],
+  exports: [ElasticsearchService],
+})
+export class ElasticsearchModule {}

+ 578 - 0
server/src/elasticsearch/elasticsearch.service.ts

@@ -0,0 +1,578 @@
+import { Injectable, Logger, OnModuleInit, Inject, forwardRef } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { Client } from '@elastic/elasticsearch';
+import { KnowledgeGroupService } from '../knowledge-group/knowledge-group.service';
+
+@Injectable()
+export class ElasticsearchService implements OnModuleInit {
+  public readonly client: Client;
+  private readonly logger = new Logger(ElasticsearchService.name);
+  private readonly indexName: string;
+
+  constructor(
+    private configService: ConfigService,
+    @Inject(forwardRef(() => KnowledgeGroupService))
+    private knowledgeGroupService: KnowledgeGroupService,
+  ) {
+    const node = this.configService.get<string>('ELASTICSEARCH_HOST'); // Changed from NODE to HOST
+    this.indexName = this.configService.get<string>(
+      'ELASTICSEARCH_INDEX',
+      'knowledge_base',
+    );
+
+    if (!node) {
+      throw new Error('ELASTICSEARCH_HOST environment variable not set.');
+    }
+
+    this.client = new Client({
+      node,
+    });
+  }
+
+  async onModuleInit() {
+    try {
+      const health = await this.client.cluster.health();
+      this.logger.log(`Elasticsearch cluster health is: ${health.status}`);
+      // 初期化時にはインデックスを作成せず、実際の使用時にモデルに基づいて動的に作成されるのを待つ
+    } catch (error) {
+      this.logger.error('Failed to connect to Elasticsearch', error);
+    }
+  }
+
+  async createIndexIfNotExists(vectorDimensions: number) {
+    const indexExists = await this.client.indices.exists({
+      index: this.indexName,
+    });
+
+    if (!indexExists) {
+      this.logger.log(
+        `インデックス ${this.indexName} を作成します。ベクトル次元数: ${vectorDimensions}`,
+      );
+      await this.createIndex(vectorDimensions);
+    } else {
+      // 既存インデックスのベクトル次元数を確認
+      const mapping = await this.client.indices.getMapping({
+        index: this.indexName,
+      });
+
+      const vectorMapping = mapping[this.indexName]?.mappings?.properties
+        ?.vector as any;
+      const existingDims = vectorMapping?.dims;
+
+      if (existingDims && existingDims !== vectorDimensions) {
+        this.logger.warn(
+          `インデックス ${this.indexName} のベクトル次元数 ${existingDims} が現在のモデル次元数 ${vectorDimensions} と一致しません。`,
+        );
+        this.logger.warn(`原因: 異なる次元数の埋め込みモデルに変更された可能性があります。システムは自動的にインデックスを再作成します。`);
+
+        // 既存インデックスを削除して再作成
+        await this.client.indices.delete({ index: this.indexName });
+        this.logger.log(`旧インデックスを正常に削除しました: ${this.indexName}`);
+
+        await this.createIndex(vectorDimensions);
+        this.logger.log(`インデックスを再作成しました: ${this.indexName} (次元数: ${vectorDimensions})`);
+      } else {
+        this.logger.log(
+          `インデックス ${this.indexName} は既に存在します。ベクトル次元数: ${existingDims || '未知'}`,
+        );
+      }
+    }
+  }
+
+  async indexDocument(
+    documentId: string,
+    content: string,
+    vector: number[],
+    metadata: any,
+  ) {
+    this.logger.log(
+      `Indexing document ${documentId}: content=${content.length} chars, vector=${vector?.length} dims`,
+    );
+
+    if (!vector || vector.length === 0) {
+      this.logger.error(`Invalid vector for document ${documentId}`);
+      throw new Error('Vector is required for indexing');
+    }
+
+    const document = {
+      content,
+      vector,
+      fileId: metadata.fileId,
+      fileName: metadata.originalName,
+      fileMimeType: metadata.mimetype,
+      chunkIndex: metadata.chunkIndex,
+      startPosition: metadata.startPosition,
+      endPosition: metadata.endPosition,
+      userId: metadata.userId,
+      createdAt: new Date(),
+    };
+
+    const result = await this.client.index({
+      index: this.indexName,
+      id: documentId,
+      document,
+    });
+
+    this.logger.log(
+      `Indexed document ${documentId} with ${vector.length}D vector`,
+    );
+    return result;
+  }
+
+  async deleteByFileId(fileId: string, userId: string) {
+    await this.client.deleteByQuery({
+      index: this.indexName,
+      query: {
+        term: { fileId },
+      },
+    });
+  }
+
+  async deleteByUserId(userId: string) {
+    // Note: This method should likely only be used by admin functionality
+    // since it deletes all data for a user
+    await this.client.deleteByQuery({
+      index: this.indexName,
+      query: {
+        term: { userId },
+      },
+    });
+  }
+
+  async searchSimilar(queryVector: number[], userId: string, topK: number = 5) {
+    try {
+      this.logger.log(
+        `Vector search: userId=${userId}, vectorDim=${queryVector?.length}, topK=${topK}`,
+      );
+
+      if (!queryVector || queryVector.length === 0) {
+        this.logger.warn('Empty query vector provided');
+        return [];
+      }
+
+      const response = await this.client.search({
+        index: this.indexName,
+        knn: {
+          field: 'vector',
+          query_vector: queryVector,
+          k: topK,
+          num_candidates: topK * 2,
+        },
+        size: topK,
+        _source: {
+          excludes: ['vector'],
+        },
+      });
+
+      const results = response.hits.hits.map((hit: any) => ({
+        id: hit._id,
+        score: this.normalizeScore(hit._score), // スコアの正規化
+        content: hit._source?.content,
+        fileId: hit._source?.fileId,
+        fileName: hit._source?.fileName,
+        chunkIndex: hit._source?.chunkIndex,
+        pageNumber: hit._source?.pageNumber, // 追加
+        startPosition: hit._source?.startPosition,
+        endPosition: hit._source?.endPosition,
+      }));
+
+      this.logger.log(
+        `Vector search completed: found ${results.length} results`,
+      );
+      return results;
+    } catch (error) {
+      this.logger.error('Vector search failed:', error);
+      return [];
+    }
+  }
+
+  async searchFullText(query: string, userId: string, topK: number = 5) {
+    try {
+      this.logger.log(
+        `Full-text search: userId=${userId}, query="${query}", topK=${topK}`,
+      );
+
+      if (!query || query.trim().length === 0) {
+        this.logger.warn('Empty query provided for full-text search');
+        return [];
+      }
+
+      const response = await this.client.search({
+        index: this.indexName,
+        query: {
+          match: {
+            content: {
+              query: query,
+              fuzziness: 'AUTO',
+            },
+          },
+        },
+        size: topK,
+        _source: {
+          excludes: ['vector'],
+        },
+      });
+
+      const results = response.hits.hits.map((hit: any) => ({
+        id: hit._id,
+        score: this.normalizeScore(hit._score), // スコアの正規化
+        content: hit._source?.content,
+        fileId: hit._source?.fileId,
+        fileName: hit._source?.fileName,
+        chunkIndex: hit._source?.chunkIndex,
+        pageNumber: hit._source?.pageNumber, // 追加
+        startPosition: hit._source?.startPosition,
+        endPosition: hit._source?.endPosition,
+      }));
+
+      this.logger.log(
+        `Full-text search completed: found ${results.length} results`,
+      );
+      return results;
+    } catch (error) {
+      this.logger.error('Full-text search failed:', error);
+      return [];
+    }
+  }
+
+  async hybridSearch(
+    queryVector: number[],
+    query: string,
+    userId: string,
+    topK: number = 5,
+    vectorWeight: number = 0.7,
+    selectedGroups?: string[], // 新規パラメータ
+    explicitFileIds?: string[], // 新規パラメータ:明示的に指定されたファイルIDリスト
+  ) {
+    // 検索範囲の決定:
+    // 1. explicitFileIds が指定されている場合(例:「ファイル対話」モード)、その範囲内のみを検索し、selectedGroups は無視する
+    // 2. それ以外で selectedGroups が指定されている場合(例:「ノートブック対話」モード)、そのグループ配下の全ファイルを取得する
+    // 3. どちらも指定されていない場合、fileIds は undefined となり、全ファイルを対象に検索する(searchXXXWithFileFilter が処理)
+
+    let fileIds: string[] | undefined;
+
+    if (explicitFileIds) {
+      this.logger.log(`明示的なファイルフィルタを使用: ${explicitFileIds.length} 個のファイル`);
+      fileIds = explicitFileIds;
+    } else if (selectedGroups && selectedGroups.length > 0) {
+      this.logger.log(`グループファイルフィルタを使用: グループ ${selectedGroups.join(', ')}`);
+      fileIds = await this.knowledgeGroupService.getFileIdsByGroups(selectedGroups, userId);
+    }
+
+    if (fileIds) {
+      this.logger.log(`最終検索対象ファイル範囲: ${fileIds.length} 個のファイル`);
+    }
+
+    // ハイブリッド検索:ベクトル検索 + 全文検索
+    const [vectorResults, textResults] = await Promise.all([
+      this.searchSimilarWithFileFilter(queryVector, userId, topK, fileIds),
+      this.searchFullTextWithFileFilter(query, userId, topK, fileIds),
+    ]);
+
+    // 結果をマージして重複を排除
+    const combinedResults = new Map();
+
+    // 向量搜索結果を追加
+    vectorResults.forEach((result) => {
+      combinedResults.set(result.id, {
+        ...result,
+        vectorScore: result.score,
+        textScore: 0,
+        combinedScore: result.score * vectorWeight,
+      });
+    });
+
+    // 全文検索結果を追加
+    textResults.forEach((result) => {
+      if (combinedResults.has(result.id)) {
+        const existing = combinedResults.get(result.id);
+        existing.textScore = result.score;
+        existing.combinedScore =
+          existing.vectorScore * vectorWeight +
+          result.score * (1 - vectorWeight);
+      } else {
+        combinedResults.set(result.id, {
+          ...result,
+          vectorScore: 0,
+          textScore: result.score,
+          combinedScore: result.score * (1 - vectorWeight),
+        });
+      }
+    });
+
+    // 正規化のためにすべての組み合わせスコアを取得
+    const allScores = Array.from(combinedResults.values()).map(r => r.combinedScore);
+    const maxScore = Math.max(...allScores, 1); // ゼロ除算を避けるため最小1
+    const minScore = Math.min(...allScores);
+
+    // 総合スコアでソートして上位 topK の結果を返す
+    return Array.from(combinedResults.values())
+      .sort((a, b) => b.combinedScore - a.combinedScore)
+      .slice(0, topK)
+      .map((result) => {
+        // combinedScoreは既に0-1の範囲にあるため、追加の正規化は不要
+        // 0-1の範囲にスコアを保つことで、実際の類似度を正確に反映
+        let finalScore = result.combinedScore;
+
+        // スコアが0-1の範囲内に収まるようにクリップ
+        finalScore = Math.max(0, Math.min(1.0, finalScore));
+
+        return {
+          id: result.id,
+          score: finalScore,
+          content: result.content,
+          fileId: result.fileId,
+          fileName: result.fileName,
+          chunkIndex: result.chunkIndex,
+          pageNumber: result.pageNumber, // 追加
+          startPosition: result.startPosition,
+          endPosition: result.endPosition,
+        };
+      });
+  }
+
+  private async createIndex(vectorDimensions: number) {
+    const mappings: any = {
+      properties: {
+        // チャンク内容
+        content: {
+          type: 'text',
+          analyzer: 'standard',
+        },
+        // ベクトルデータ
+        vector: {
+          type: 'dense_vector',
+          dims: vectorDimensions,
+          index: true,
+          similarity: 'cosine',
+        },
+        // ファイル関連情報
+        fileId: { type: 'keyword' },
+        fileName: { type: 'keyword' },
+        fileMimeType: { type: 'keyword' },
+
+        // チャンク情報
+        chunkIndex: { type: 'integer' },
+        startPosition: { type: 'integer' },
+        endPosition: { type: 'integer' },
+
+        // ユーザー情報
+        userId: { type: 'keyword' },
+
+        // タイムスタンプ
+        createdAt: { type: 'date' },
+      },
+    };
+
+    await this.client.indices.create({
+      index: this.indexName,
+      mappings,
+    });
+
+    this.logger.log(
+      `インデックス ${this.indexName} を正常に作成しました。ベクトル次元数: ${vectorDimensions}`,
+    );
+  }
+
+  /**
+   * Elasticsearch スコアを 0-1 の範囲に正規化する
+   * Elasticsearch のスコアは 1.0 を超える可能性があるため、正規化が必要
+   * ただし、kNN検索の類似度スコアは既に0-1の範囲にある(cosine similarity)ので、
+   * 特別な正規化は不要。必要に応じて最小値保護のみ行う。
+   */
+  private normalizeScore(rawScore: number): number {
+    if (!rawScore || rawScore <= 0) return 0; // 最小値は0
+
+    // kNN検索の場合は既に0-1の範囲にあるので、1を超えないようにクリップ
+    // cosine similarityの最大値は1なので、1以上になった場合は1とする
+    return Math.min(1.0, rawScore);
+  }
+
+  // ファイルフィルタ付きのベクトル検索
+  private async searchSimilarWithFileFilter(
+    queryVector: number[],
+    userId: string,
+    topK: number = 5,
+    fileIds?: string[],
+  ) {
+    try {
+      this.logger.log(
+        `Vector search with filter: userId=${userId}, vectorDim=${queryVector?.length}, topK=${topK}, fileIds=${fileIds?.length || 'all'}`,
+      );
+
+      if (!queryVector || queryVector.length === 0) {
+        this.logger.warn('Empty query vector provided');
+        return [];
+      }
+
+      let filter: any;
+      if (fileIds) {
+        if (fileIds.length > 0) {
+          filter = {
+            terms: { fileId: fileIds },
+          };
+        } else {
+          // ID列表存在但为空,表示强制不匹配任何东西
+          filter = { match_none: {} };
+        }
+      } else {
+        filter = {}; // No filter when no file IDs specified
+      }
+
+      const queryBody: any = {
+        index: this.indexName,
+        knn: {
+          field: 'vector',
+          query_vector: queryVector,
+          k: topK,
+          num_candidates: topK * 2,
+        },
+        size: topK,
+        _source: {
+          excludes: ['vector'],
+        },
+      };
+
+      if (filter && Object.keys(filter).length > 0) {
+        queryBody.knn.filter = filter;
+      }
+
+      const response = await this.client.search(queryBody);
+
+      const results = response.hits.hits.map((hit: any) => ({
+        id: hit._id,
+        score: this.normalizeScore(hit._score),
+        content: hit._source?.content,
+        fileId: hit._source?.fileId,
+        fileName: hit._source?.fileName,
+        chunkIndex: hit._source?.chunkIndex,
+        pageNumber: hit._source?.pageNumber, // 追加
+        startPosition: hit._source?.startPosition,
+        endPosition: hit._source?.endPosition,
+      }));
+
+      this.logger.log(
+        `Vector search completed: found ${results.length} results`,
+      );
+      return results;
+    } catch (error) {
+      this.logger.error('Vector search failed:', error);
+      return [];
+    }
+  }
+
+  // ファイルフィルタ付きの全文検索
+  private async searchFullTextWithFileFilter(
+    query: string,
+    userId: string,
+    topK: number = 5,
+    fileIds?: string[],
+  ) {
+    try {
+      this.logger.log(
+        `Full-text search with filter: userId=${userId}, query="${query}", topK=${topK}, fileIds=${fileIds?.length || 'all'}`,
+      );
+
+      if (!query || query.trim().length === 0) {
+        this.logger.warn('Empty query provided for full-text search');
+        return [];
+      }
+
+      const mustClause: any[] = [
+        {
+          match: {
+            content: {
+              query: query,
+              fuzziness: 'AUTO',
+            },
+          },
+        },
+      ];
+
+      const filter: any[] = [];
+      if (fileIds) {
+        if (fileIds.length > 0) {
+          filter.push({ terms: { fileId: fileIds } });
+        } else {
+          // ID列表存在但为空,表示强制不匹配任何东西
+          filter.push({ match_none: {} });
+        }
+      }
+
+      const queryBody: any = {
+        index: this.indexName,
+        query: {
+          bool: {
+            must: mustClause,
+          },
+        },
+        size: topK,
+        _source: {
+          excludes: ['vector'],
+        },
+      };
+
+      if (filter.length > 0) {
+        queryBody.query.bool.filter = filter;
+      }
+
+      const response = await this.client.search(queryBody);
+
+      const results = response.hits.hits.map((hit: any) => ({
+        id: hit._id,
+        score: this.normalizeScore(hit._score),
+        content: hit._source?.content,
+        fileId: hit._source?.fileId,
+        fileName: hit._source?.fileName,
+        chunkIndex: hit._source?.chunkIndex,
+        pageNumber: hit._source?.pageNumber, // 追加
+        startPosition: hit._source?.startPosition,
+        endPosition: hit._source?.endPosition,
+      }));
+
+      this.logger.log(
+        `Full-text search completed: found ${results.length} results`,
+      );
+      return results;
+    } catch (error) {
+      this.logger.error('Full-text search failed:', error);
+      return [];
+    }
+  }
+
+  /**
+   * 指定されたファイルのすべてのチャンクを取得
+   */
+  async getFileChunks(fileId: string) {
+    try {
+      this.logger.log(`Getting chunks for file ${fileId}`);
+
+      const response = await this.client.search({
+        index: this.indexName,
+        query: {
+          term: { fileId },
+        },
+        sort: [{ chunkIndex: 'asc' }],
+        size: 10000, // 単一ファイルが 10000 チャンクを超えないと想定
+        _source: {
+          excludes: ['vector'], // 転送量を減らすため、ベクトルデータは返さない
+        },
+      });
+
+      const chunks = response.hits.hits.map((hit: any) => ({
+        id: hit._id,
+        chunkIndex: hit._source.chunkIndex,
+        content: hit._source.content,
+        startPosition: hit._source.startPosition,
+        endPosition: hit._source.endPosition,
+        fileName: hit._source.fileName,
+      }));
+
+      this.logger.log(`Found ${chunks.length} chunks for file ${fileId}`);
+      return chunks;
+    } catch (error) {
+      this.logger.error(`Failed to get chunks for file ${fileId}`, error);
+      return [];
+    }
+  }
+}

+ 9 - 0
server/src/i18n/i18n.module.ts

@@ -0,0 +1,9 @@
+import { Global, Module } from '@nestjs/common';
+import { I18nService } from './i18n.service';
+
+@Global()
+@Module({
+    providers: [I18nService],
+    exports: [I18nService],
+})
+export class I18nModule { }

+ 227 - 0
server/src/i18n/i18n.service.ts

@@ -0,0 +1,227 @@
+import { Injectable } from '@nestjs/common';
+import { errorMessages, logMessages, statusMessages } from './messages';
+
+@Injectable()
+export class I18nService {
+  private readonly defaultLanguage = 'ja'; // プロジェクト要件に従い、日本語をデフォルトとして使用
+
+  getErrorMessage(key: string, language?: string): string {
+    const lang = language || this.defaultLanguage;
+    return errorMessages[lang]?.[key] || errorMessages[this.defaultLanguage][key] || key;
+  }
+
+  getLogMessage(key: string, language?: string): string {
+    const lang = language || this.defaultLanguage;
+    return logMessages[lang]?.[key] || logMessages[this.defaultLanguage][key] || key;
+  }
+
+  getStatusMessage(key: string, language?: string): string {
+    const lang = language || this.defaultLanguage;
+    return statusMessages[lang]?.[key] || statusMessages[this.defaultLanguage][key] || key;
+  }
+
+  // 汎用メッセージ取得メソッド、順次検索
+  getMessage(key: string, language?: string): string {
+    const lang = language || this.defaultLanguage;
+    // ステータスメッセージ、エラーメッセージ、ログメッセージの順に検索
+    return statusMessages[lang]?.[key] ||
+      statusMessages[this.defaultLanguage][key] ||
+      errorMessages[lang]?.[key] ||
+      errorMessages[this.defaultLanguage][key] ||
+      logMessages[lang]?.[key] ||
+      logMessages[this.defaultLanguage][key] ||
+      key;
+  }
+
+  // メッセージの取得とフォーマット
+  formatMessage(key: string, args: Record<string, any>, language?: string): string {
+    let message = this.getMessage(key, language);
+    for (const [argKey, argValue] of Object.entries(args)) {
+      message = message.replace(new RegExp(`\\{${argKey}\\}`, 'g'), String(argValue));
+    }
+    return message;
+  }
+
+  // サポートされている言語リストを取得
+  getSupportedLanguages(): string[] {
+    return Object.keys(errorMessages);
+  }
+
+  // 言語がサポートされているか確認
+  isLanguageSupported(language: string): boolean {
+    return this.getSupportedLanguages().includes(language);
+  }
+
+  // システムプロンプトを取得
+  getPrompt(lang: string = this.defaultLanguage, type: 'withContext' | 'withoutContext' = 'withContext', hasKnowledgeGroup: boolean = false): string {
+    if (lang === 'zh') {
+      return type === 'withContext' ? `
+基于以下知识库内容回答用户问题。
+
+${hasKnowledgeGroup ? `**重要提示**: 用户已选择特定知识组,请严格基于以下知识库内容回答。如果知识库中没有相关信息,请明确告知用户"知识库中未找到相关内容,以下是基于模型的一般性回答:",然后再提供答案。
+
+` : ''}知识库内容:
+{context}
+
+历史对话:
+{history}
+
+用户问题:{question}
+
+请用中文回答,并严格遵循以下 Markdown 格式要求:
+
+1. **段落与结构**:
+   - 使用清晰的段落分隔,每个要点之间空一行
+   - 使用标题(## 或 ###)组织长回答
+
+2. **文本格式**:
+   - 使用 **粗体** 强调重要概念和关键词
+   - 使用列表(- 或 1.)组织多个要点
+   - 使用 \`代码\` 标记技术术语、命令、文件名
+
+3. **代码展示**:
+   - 使用代码块展示代码,并指定语言:
+     \`\`\`python
+     def example():
+         return "示例"
+     \`\`\`
+   - 支持的语言:python, javascript, typescript, java, bash, sql 等
+
+4. **流程图与图表**:
+   - 使用 Mermaid 语法展示流程图、时序图等:
+     \`\`\`mermaid
+     graph LR
+         A[开始] --> B[处理]
+         B --> C[结束]
+     \`\`\`
+   - 适用场景:流程说明、架构图、状态图、时序图
+
+5. **其他要求**:
+   - 回答要简洁明了,避免冗长
+   - 如果有多个步骤,使用编号列表
+   - 使用表格展示对比信息(如果适用)
+` : `
+作为一个智能助手,请回答用户的问题。
+
+历史对话:
+{history}
+
+用户问题:{question}
+
+请用中文回答。
+`;
+    } else if (lang === 'en') {
+      return type === 'withContext' ? `
+Answer the user's question based on the following knowledge base content.
+
+${hasKnowledgeGroup ? `**IMPORTANT**: The user has selected a specific knowledge group. Please answer strictly based on the knowledge base content below. If there is no relevant information in the knowledge base, please explicitly inform the user "No relevant content found in the selected knowledge group. The following is a general answer based on the model:", and then provide your answer.
+
+` : ''}Knowledge base content:
+{context}
+
+Conversation history:
+{history}
+
+User question: {question}
+
+Please answer in English and strictly follow these Markdown formatting guidelines:
+
+1. **Paragraphs & Structure**:
+   - Use clear paragraph breaks with blank lines between key points
+   - Use headings (## or ###) to organize longer answers
+
+2. **Text Formatting**:
+   - Use **bold** to emphasize important concepts and keywords
+   - Use lists (- or 1.) to organize multiple points
+   - Use \`code\` to mark technical terms, commands, file names
+
+3. **Code Display**:
+   - Use code blocks with language specification:
+     \`\`\`python
+     def example():
+         return "example"
+     \`\`\`
+   - Supported languages: python, javascript, typescript, java, bash, sql, etc.
+
+4. **Diagrams & Charts**:
+   - Use Mermaid syntax for flowcharts, sequence diagrams, etc.:
+     \`\`\`mermaid
+     graph LR
+         A[Start] --> B[Process]
+         B --> C[End]
+     \`\`\`
+   - Use cases: process flows, architecture diagrams, state diagrams, sequence diagrams
+
+5. **Other Requirements**:
+   - Keep answers concise and clear
+   - Use numbered lists for multi-step processes
+   - Use tables for comparison information (if applicable)
+` : `
+As an intelligent assistant, please answer the user's question.
+
+Conversation history:
+{history}
+
+User question: {question}
+
+Please answer in English.
+`;
+    } else { // 默认为日语,符合项目要求
+      return type === 'withContext' ? `
+以下のナレッジベースの内容に基づいてユーザーの質問に答えてください。
+${hasKnowledgeGroup ? `
+**重要**: ユーザーが特定の知識グループを選択しました。以下のナレッジベースの内容に厳密に基づいて回答してください。ナレッジベースに関連情報がない場合は、「選択された知識グループに関連する内容が見つかりませんでした。以下はモデルに基づく一般的な回答です:」とユーザーに明示的に伝えてから、回答を提供してください。
+` : ''}
+ナレッジベースの内容:
+{context}
+
+会話履歴:
+{history}
+
+ユーザーの質問:{question}
+
+日本語で回答してください。以下の Markdown 書式要件に厳密に従ってください:
+
+1. **段落と構造**:
+   - 明確な段落分けを使用し、要点間に空行を入れる
+   - 長い回答には見出し(## または ###)を使用
+
+2. **テキスト書式**:
+   - 重要な概念やキーワードを強調するために **太字** を使用
+   - 複数のポイントを整理するためにリスト(- または 1.)を使用
+   - 技術用語、コマンド、ファイル名をマークするために \`コード\` を使用
+
+3. **コード表示**:
+   - 言語を指定してコードブロックを使用:
+     \`\`\`python
+     def example():
+         return "例"
+     \`\`\`
+   - 対応言語:python, javascript, typescript, java, bash, sql など
+
+4. **図表とチャート**:
+   - フローチャート、シーケンス図などに Mermaid 構文を使用:
+     \`\`\`mermaid
+     graph LR
+         A[開始] --> B[処理]
+         B --> C[終了]
+     \`\`\`
+   - 使用例:プロセスフロー、アーキテクチャ図、状態図、シーケンス図
+
+5. **その他の要件**:
+   - 簡潔で明確な回答を心がける
+   - 複数のステップがある場合は番号付きリストを使用
+   - 比較情報には表を使用(該当する場合)
+` : `
+インテリジェントアシスタントとして、ユーザーの質問に答えてください。
+
+会話履歴:
+{history}
+
+ユーザーの質問:{question}
+
+日本語で回答してください。
+`;
+    }
+  }
+}

+ 264 - 0
server/src/i18n/messages.ts

@@ -0,0 +1,264 @@
+export const errorMessages = {
+  zh: {
+    noEmbeddingModel: '请先在系统设置中配置嵌入模型',
+    searchFailed: '搜索知识库失败,将基于一般知识回答...',
+    invalidApiKey: 'API密钥无效',
+    fileNotFound: '未找到文件',
+    insufficientQuota: '配额不足',
+    modelNotConfigured: '未配置模型',
+    visionModelNotConfigured: '未配置视觉模型',
+    embeddingDimensionMismatch: '嵌入维度不匹配',
+    uploadNoFile: '未上传文件',
+    uploadSizeExceeded: '文件大小超过限制: {size}, 最大允许: {max}',
+    uploadModelRequired: '必须选择嵌入模型',
+    uploadTypeUnsupported: '不支持的文件格式: {type}',
+    chunkOverflow: '切片大小 {size} 超过上限 {max} ({reason})。已自动调整',
+    chunkUnderflow: '切片大小 {size} 小于最小值 {min}。已自动调整',
+    overlapOverflow: '重叠大小 {size} 超过上限 {max}。已自动调整',
+    overlapRatioExceeded: '重叠大小 {size} 超过切片大小的50% ({max})。已自动调整',
+    batchOverflowWarning: '建议切片大小不超过 {safeSize} 以避免批量处理溢出 (当前: {size}, 模型限制的 {percent}%)',
+    estimatedChunkCountExcessive: '预计切片数量过多 ({count}),处理可能较慢',
+    contentAndTitleRequired: '内容和标题为必填项',
+    embeddingModelNotFound: '找不到嵌入模型 {id} 或类型不是 embedding',
+    ocrFailed: '提取文本失败: {message}',
+    noImageUploaded: '未上传图片',
+  },
+  ja: {
+    noEmbeddingModel: '先にシステム設定で埋め込みモデルを設定してください',
+    searchFailed: 'ナレッジベース検索に失敗しました。一般的な知識に基づいて回答します...',
+    invalidApiKey: 'APIキーが無効です',
+    fileNotFound: 'ファイルが見つかりません',
+    insufficientQuota: '利用枠が不足しています',
+    modelNotConfigured: 'モデルが設定されていません',
+    visionModelNotConfigured: 'ビジョンモデルが設定されていません',
+    embeddingDimensionMismatch: '埋め込み次元数が一致しません',
+    uploadNoFile: 'ファイルがアップロードされていません',
+    uploadSizeExceeded: 'ファイルサイズが制限を超えています: {size}, 最大許容: {max}',
+    uploadModelRequired: '埋め込みモデルを選択する必要があります',
+    uploadTypeUnsupported: 'サポートされていないファイル形式です: {type}',
+    chunkOverflow: 'チャンクサイズ {size} が上限 {max} ({reason}) を超えています。自動調整されました',
+    chunkUnderflow: 'チャンクサイズ {size} が最小値 {min} 未満です。自動調整されました',
+    overlapOverflow: '重なりサイズ {size} が上限 {max} を超えています。自動調整されました',
+    overlapRatioExceeded: '重なりサイズ {size} がチャンクサイズの50% ({max}) を超えています。自動調整されました',
+    batchOverflowWarning: 'バッチ処理のオーバーフローを避けるため、チャンクサイズを {safeSize} 以下にすることをお勧めします (現在: {size}, モデル制限の {percent}%)',
+    estimatedChunkCountExcessive: '推定チャンク数が多すぎます ({count})。処理に時間がかかる可能性があります',
+    contentAndTitleRequired: '内容とタイトルは必須です',
+    embeddingModelNotFound: '埋め込みモデル {id} が見つかりません、またはタイプが embedding ではありません',
+    ocrFailed: 'テキストの抽出に失敗しました: {message}',
+    noImageUploaded: '画像がアップロードされていません',
+  },
+  en: {
+    noEmbeddingModel: 'Please configure embedding model in system settings first',
+    searchFailed: 'Knowledge base search failed, will answer based on general knowledge...',
+    invalidApiKey: 'Invalid API key',
+    fileNotFound: 'File not found',
+    insufficientQuota: 'Insufficient quota',
+    modelNotConfigured: 'Model not configured',
+    visionModelNotConfigured: 'Vision model not configured',
+    embeddingDimensionMismatch: 'Embedding dimensions mismatch',
+    uploadNoFile: 'No file uploaded',
+    uploadSizeExceeded: 'File size exceeds limit: {size}, Max allowed: {max}',
+    uploadModelRequired: 'Embedding model must be selected',
+    uploadTypeUnsupported: 'Unsupported file type: {type}',
+    chunkOverflow: 'Chunk size {size} exceeds limit {max} ({reason}). Auto-adjusted',
+    chunkUnderflow: 'Chunk size {size} is below minimum {min}. Auto-adjusted',
+    overlapOverflow: 'Overlap size {size} exceeds limit {max}. Auto-adjusted',
+    overlapRatioExceeded: 'Overlap size {size} exceeds 50% of chunk size ({max}). Auto-adjusted',
+    batchOverflowWarning: 'Recommended chunk size below {safeSize} to avoid batch overflow (Current: {size}, {percent}% of model limit)',
+    estimatedChunkCountExcessive: 'Estimated chunk count is too high ({count}). Processing may be slow',
+    contentAndTitleRequired: 'Content and Title are required',
+    embeddingModelNotFound: 'Embedding model {id} not found or type is not embedding',
+    ocrFailed: 'Failed to extract text: {message}',
+    noImageUploaded: 'No image uploaded',
+  }
+};
+
+export const logMessages = {
+  zh: {
+    processingFile: '处理文件: {name} ({size})',
+    indexingComplete: '索引完成: {id}',
+    vectorizingFile: '向量化文件: ',
+    searchQuery: '搜索查询: ',
+    modelCall: '[模型调用] 类型: {type}, 模型: {model}, 用户: {user}',
+    memoryStatus: '内存状态: ',
+    uploadSuccess: '文件上传成功。正在后台索引',
+    overlapAdjusted: '重叠大小超过切片大小的50%。已自动调整为 {newSize}',
+    environmentLimit: '环境变量限制',
+    modelLimit: '模型限制',
+    configLoaded: '数据库模型配置加载: {name} ({id})',
+    batchSizeAdjusted: '批量大小从 {old} 调整为 {new} (模型限制: {limit})',
+    dimensionMismatch: '模型 {id} 维度不匹配: 预期 {expected}, 实际 {actual}',
+  },
+  ja: {
+    processingFile: 'ファイル処理中: {name} ({size})',
+    indexingComplete: 'インデックス完了: {id}',
+    vectorizingFile: 'ファイルベクトル化中: ',
+    searchQuery: '検索クエリ: ',
+    modelCall: '[モデル呼び出し] タイプ: {type}, モデル: {model}, ユーザー: {user}',
+    memoryStatus: 'メモリ状態: ',
+    uploadSuccess: 'ファイルが正常にアップロードされました。バックグラウンドでインデックス処理を実行中です',
+    overlapAdjusted: 'オーバーラップサイズがチャンクサイズの50%を超えています。自動的に {newSize} に調整されました',
+    environmentLimit: '環境変数の制限',
+    modelLimit: 'モデルの制限',
+    configLoaded: 'データベースからモデル設定を読み込みました: {name} ({id})',
+    batchSizeAdjusted: 'バッチサイズを {old} から {new} に調整しました (モデル制限: {limit})',
+    dimensionMismatch: 'モデル {id} の次元が一致しません: 期待値 {expected}, 実際 {actual}',
+  },
+  en: {
+    processingFile: 'Processing file: {name} ({size})',
+    indexingComplete: 'Indexing complete: {id}',
+    vectorizingFile: 'Vectorizing file: ',
+    searchQuery: 'Search query: ',
+    modelCall: '[Model call] Type: {type}, Model: {model}, User: {user}',
+    memoryStatus: 'Memory status: ',
+    uploadSuccess: 'File uploaded successfully. Indexing in background',
+    overlapAdjusted: 'Overlap size exceeds 50% of chunk size. Auto-adjusted to {newSize}',
+    environmentLimit: 'Environment variable limit',
+    modelLimit: 'Model limit',
+    configLoaded: 'Model config loaded from DB: {name} ({id})',
+    batchSizeAdjusted: 'Batch size adjusted from {old} to {new} (Model limit: {limit})',
+    dimensionMismatch: 'Model {id} dimension mismatch: Expected {expected}, Actual {actual}',
+  }
+};
+
+export const statusMessages = {
+  zh: {
+    searching: '正在搜索知识库...',
+    noResults: '未找到相关知识,将基于一般知识回答...',
+    searchFailed: '知识库搜索失败,将基于一般知识回答...',
+    generatingResponse: '正在生成回答',
+    files: '个文件',
+    notebooks: '个笔记本',
+    all: '全部',
+    items: '个',
+    searchResults: '搜索结果',
+    relevantInfoFound: '条相关信息找到',
+    searchHits: '搜索命中',
+    relevance: '相关度',
+    sourceFiles: '源文件',
+    searchScope: '搜索范围',
+    error: '错误',
+    creatingHistory: '创建新对话历史: ',
+    searchingModelById: '根据ID搜索模型: ',
+    searchModelFallback: '未找到指定的嵌入模型。使用第一个可用模型。',
+    noEmbeddingModelFound: '找不到嵌入模型设置',
+    usingEmbeddingModel: '使用的嵌入模型: ',
+    startingSearch: '开始搜索知识库...',
+    searchResultsCount: '搜索结果数: ',
+    searchFailedLog: '搜索失败',
+    modelCall: '[模型调用]',
+    chatStreamError: '聊天流错误',
+    assistStreamError: '辅助流错误',
+    file: '文件',
+    content: '内容',
+    userLabel: '用户',
+    assistantLabel: '助手',
+    intelligentAssistant: '您是智能写作助手。',
+    searchString: '搜索字符串: ',
+    embeddingModelIdNotProvided: '未提供嵌入模型ID',
+    generatingEmbeddings: '生成嵌入向量...',
+    embeddingsGenerated: '嵌入向量生成完成',
+    dimensions: '维度',
+    performingHybridSearch: '执行混合搜索...',
+    esSearchCompleted: 'ES搜索完成',
+    resultsCount: '结果数',
+    hybridSearchFailed: '混合搜索失败',
+    getContextForTopicFailed: '获取主题上下文失败',
+    noLLMConfigured: '用户未配置LLM模型',
+    simpleChatGenerationError: '简单聊天生成错误',
+    noMatchInKnowledgeGroup: '所选知识组中未找到相关内容,以下是基于模型的一般性回答:',
+    uploadTextSuccess: '笔记内容已接收。正在后台索引',
+  },
+  ja: {
+    searching: 'ナレッジベースを検索中...',
+    noResults: '関連する知識が見つかりませんでした。一般的な知識に基づいて回答します...',
+    searchFailed: 'ナレッジベース検索に失敗しました。一般的な知識に基づいて回答します...',
+    generatingResponse: '回答を生成中',
+    files: '個のファイル',
+    notebooks: '個のノートブック',
+    all: 'すべて',
+    items: '件',
+    searchResults: '検索結果',
+    relevantInfoFound: '件の関連情報が見つかりました',
+    searchHits: '検索ヒット',
+    relevance: '関連度',
+    sourceFiles: '元ファイル',
+    searchScope: '検索範囲',
+    error: 'エラー',
+    creatingHistory: '新規対話履歴を作成: ',
+    searchingModelById: 'selectedEmbeddingId に基づいてモデルを検索: ',
+    searchModelFallback: '指定された埋め込みモデルが見つかりません。最初に使用可能なモデルを使用します。',
+    noEmbeddingModelFound: '埋め込みモデルの設定が見つかりません',
+    usingEmbeddingModel: '使用する埋め込みモデル: ',
+    startingSearch: 'ナレッジベースの検索を開始...',
+    searchResultsCount: '検索結果数: ',
+    searchFailedLog: '検索失敗',
+    chatStreamError: 'チャットストリームエラー',
+    assistStreamError: 'アシストストリームエラー',
+    file: 'ファイル',
+    content: '内容',
+    userLabel: 'ユーザー',
+    assistantLabel: 'アシスタント',
+    intelligentAssistant: 'あなたはインテリジェントな執筆アシスタントです。',
+    searchString: '検索文字列: ',
+    embeddingModelIdNotProvided: '埋め込みモデルIDが提供されていません',
+    generatingEmbeddings: '埋め込みベクトルを生成中...',
+    embeddingsGenerated: '埋め込みベクトルの生成が完了しました',
+    dimensions: '次元数',
+    performingHybridSearch: 'ES 混合検索を実行中...',
+    esSearchCompleted: 'ES 検索が完了しました',
+    resultsCount: '結果数',
+    hybridSearchFailed: '混合検索に失敗しました',
+    getContextForTopicFailed: 'トピックのコンテキスト取得に失敗しました',
+    noLLMConfigured: 'ユーザーにLLMモデルが設定されていません',
+    simpleChatGenerationError: '簡易チャット生成エラー',
+    noMatchInKnowledgeGroup: '選択された知識グループに関連する内容が見つかりませんでした。以下はモデルに基づく一般的な回答です:',
+    uploadTextSuccess: 'ノート内容を受け取りました。バックグラウンドでインデックス処理を実行中です',
+  },
+  en: {
+    searching: 'Searching knowledge base...',
+    noResults: 'No relevant knowledge found, will answer based on general knowledge...',
+    searchFailed: 'Knowledge base search failed, will answer based on general knowledge...',
+    generatingResponse: 'Generating response',
+    files: ' files',
+    notebooks: ' notebooks',
+    all: 'all',
+    items: '',
+    searchResults: 'Search results',
+    relevantInfoFound: ' relevant info found',
+    searchHits: 'Search hits',
+    relevance: 'Relevance',
+    sourceFiles: 'Source files',
+    searchScope: 'Search scope',
+    error: 'Error',
+    creatingHistory: 'Creating new chat history: ',
+    searchingModelById: 'Searching model by ID: ',
+    searchModelFallback: 'Specified embedding model not found. Using first available model.',
+    noEmbeddingModelFound: 'No embedding model settings found',
+    usingEmbeddingModel: 'Using embedding model: ',
+    startingSearch: 'Starting knowledge base search...',
+    searchResultsCount: 'Search results count: ',
+    searchFailedLog: 'Search failed',
+    chatStreamError: 'Chat stream error',
+    assistStreamError: 'Assist stream error',
+    file: 'File',
+    content: 'Content',
+    userLabel: 'User',
+    assistantLabel: 'Assistant',
+    intelligentAssistant: 'You are an intelligent writing assistant.',
+    searchString: 'Search string: ',
+    embeddingModelIdNotProvided: 'Embedding model ID not provided',
+    generatingEmbeddings: 'Generating embeddings...',
+    embeddingsGenerated: 'Embeddings generated successfully',
+    dimensions: 'dimensions',
+    performingHybridSearch: 'Performing hybrid search...',
+    esSearchCompleted: 'ES search completed',
+    resultsCount: 'Results count',
+    hybridSearchFailed: 'Hybrid search failed',
+    getContextForTopicFailed: 'getContextForTopic failed',
+    noLLMConfigured: 'No LLM model configured for user',
+    simpleChatGenerationError: 'Simple chat generation error',
+    noMatchInKnowledgeGroup: 'No relevant content found in the selected knowledge group. The following is a general answer based on the model:',
+    uploadTextSuccess: 'Note content received. Indexing in background',
+  }
+};

+ 31 - 0
server/src/import-task/import-task.controller.ts

@@ -0,0 +1,31 @@
+import { Controller, Post, Get, Body, Request, UseGuards } from '@nestjs/common';
+import { ImportTaskService } from './import-task.service';
+import { JwtAuthGuard } from '../auth/jwt-auth.guard';
+import { AdminGuard } from '../auth/admin.guard';
+
+@Controller('import-tasks')
+@UseGuards(JwtAuthGuard)
+export class ImportTaskController {
+    constructor(private readonly taskService: ImportTaskService) { }
+
+    @Post()
+    @UseGuards(AdminGuard)  // Only admin users can create import tasks
+    async create(@Request() req, @Body() body: any) {
+        return this.taskService.create({
+            sourcePath: body.sourcePath,
+            targetGroupId: body.targetGroupId,
+            targetGroupName: body.targetGroupName,
+            embeddingModelId: body.embeddingModelId,
+            scheduledAt: body.scheduledAt ? new Date(body.scheduledAt) : undefined,
+            chunkSize: body.chunkSize,
+            chunkOverlap: body.chunkOverlap,
+            mode: body.mode,
+            userId: req.user.id,
+        });
+    }
+
+    @Get()
+    async findAll(@Request() req) {
+        return this.taskService.findAll(req.user.id);
+    }
+}

+ 46 - 0
server/src/import-task/import-task.entity.ts

@@ -0,0 +1,46 @@
+import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
+
+@Entity()
+export class ImportTask {
+    @PrimaryGeneratedColumn('uuid')
+    id: string;
+
+    @Column()
+    sourcePath: string;
+
+    @Column({ nullable: true })
+    targetGroupId: string; // If null, creates new group
+
+    @Column({ nullable: true })
+    targetGroupName: string; // Used if creating new group
+
+    @Column()
+    userId: string;
+
+    @Column({ nullable: true })
+    scheduledAt: Date;
+
+    @Column({ default: 'PENDING' })
+    status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED';
+
+    @Column({ type: 'text', nullable: true })
+    logs: string;
+
+    @Column({ nullable: true })
+    embeddingModelId: string;
+
+    @Column({ nullable: true, default: 500 })
+    chunkSize: number;
+
+    @Column({ nullable: true, default: 50 })
+    chunkOverlap: number;
+
+    @Column({ nullable: true, default: 'fast' })
+    mode: string;
+
+    @CreateDateColumn()
+    createdAt: Date;
+
+    @UpdateDateColumn()
+    updatedAt: Date;
+}

+ 18 - 0
server/src/import-task/import-task.module.ts

@@ -0,0 +1,18 @@
+import { Module } from '@nestjs/common';
+import { TypeOrmModule } from '@nestjs/typeorm';
+import { ImportTask } from './import-task.entity';
+import { ImportTaskService } from './import-task.service';
+import { ImportTaskController } from './import-task.controller';
+import { KnowledgeBaseModule } from '../knowledge-base/knowledge-base.module';
+import { KnowledgeGroupModule } from '../knowledge-group/knowledge-group.module';
+
+@Module({
+    imports: [
+        TypeOrmModule.forFeature([ImportTask]),
+        KnowledgeBaseModule,
+        KnowledgeGroupModule,
+    ],
+    controllers: [ImportTaskController],
+    providers: [ImportTaskService],
+})
+export class ImportTaskModule { }

+ 232 - 0
server/src/import-task/import-task.service.ts

@@ -0,0 +1,232 @@
+import { Injectable, Logger } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { Repository, LessThanOrEqual } from 'typeorm';
+import { ConfigService } from '@nestjs/config';
+import { ImportTask } from './import-task.entity';
+import { Cron, CronExpression } from '@nestjs/schedule';
+import { KnowledgeBaseService } from '../knowledge-base/knowledge-base.service';
+import { KnowledgeGroupService } from '../knowledge-group/knowledge-group.service';
+import * as fs from 'fs';
+import * as path from 'path';
+
+@Injectable()
+export class ImportTaskService {
+    private readonly logger = new Logger(ImportTaskService.name);
+
+    constructor(
+        @InjectRepository(ImportTask)
+        private taskRepository: Repository<ImportTask>,
+        private kbService: KnowledgeBaseService,
+        private groupService: KnowledgeGroupService,
+        private configService: ConfigService,
+    ) { }
+
+    async create(taskData: Partial<ImportTask>): Promise<ImportTask> {
+        const task = this.taskRepository.create(taskData);
+        const savedTask = await this.taskRepository.save(task);
+
+        // If no scheduled time or scheduled time is in the past, execute immediately (async)
+        if (!task.scheduledAt || task.scheduledAt <= new Date()) {
+            this.executeTask(savedTask.id).catch(err =>
+                this.logger.error(`Immediate execution failed to start for task ${savedTask.id}`, err)
+            );
+        }
+
+        return savedTask;
+    }
+
+    async findAll(userId: string): Promise<ImportTask[]> {
+        return this.taskRepository.find({
+            where: { userId },
+            order: { createdAt: 'DESC' },
+        });
+    }
+
+    @Cron(CronExpression.EVERY_MINUTE)
+    async handleScheduledTasks() {
+        this.logger.debug('Checking for scheduled import tasks...');
+        const now = new Date();
+
+        // Find pending tasks that are due
+        const tasks = await this.taskRepository.find({
+            where: {
+                status: 'PENDING',
+                scheduledAt: LessThanOrEqual(now),
+            },
+        });
+
+        for (const task of tasks) {
+            this.logger.log(`Starting scheduled task ${task.id}`);
+            // Execute without awaiting to allow parallel execution of multiple tasks
+            this.executeTask(task.id).catch(err =>
+                this.logger.error(`Scheduled execution failed to start for task ${task.id}`, err)
+            );
+        }
+    }
+
+    private async executeTask(taskId: string) {
+        this.logger.debug(`Executing task ${taskId}`);
+        const task = await this.taskRepository.findOne({ where: { id: taskId } });
+        if (!task) {
+            this.logger.warn(`Task ${taskId} not found.`);
+            return;
+        }
+
+        if (task.status === 'PROCESSING' || task.status === 'COMPLETED') {
+            this.logger.debug(`Task ${taskId} is already ${task.status}, skipping execution.`);
+            return;
+        }
+
+        await this.updateStatus(taskId, 'PROCESSING', 'Starting import...');
+        this.logger.debug(`Task ${taskId} status updated to PROCESSING.`);
+
+        try {
+            if (!fs.existsSync(task.sourcePath)) {
+                throw new Error(`Directory not found: ${task.sourcePath}`);
+            }
+
+            // 1. Prepare Target Group
+            this.logger.debug(`Task ${taskId}: Preparing target group.`);
+            let groupId = task.targetGroupId;
+            if (!groupId && task.targetGroupName) {
+                // Create new group
+                const group = await this.groupService.create(task.userId, {
+                    name: task.targetGroupName,
+                    description: `Imported from ${task.sourcePath}`,
+                    color: '#0078D4', // Default blue
+                });
+                groupId = group.id;
+                await this.appendLog(taskId, `Created new group: ${task.targetGroupName}`);
+                this.logger.debug(`Task ${taskId}: Created new group ${group.id} - ${task.targetGroupName}`);
+            } else if (!groupId) {
+                throw new Error('No target group specified');
+            }
+            this.logger.debug(`Task ${taskId}: Target group ID is ${groupId}.`);
+
+            // 2. Scan Files
+            await this.appendLog(taskId, `Scanning directory: ${task.sourcePath}`);
+            this.logger.debug(`Task ${taskId}: Scanning directory ${task.sourcePath}.`);
+            const filesToImport = this.scanDir(task.sourcePath);
+            await this.appendLog(taskId, `Found ${filesToImport.length} markdown/text files.`);
+            this.logger.debug(`Task ${taskId}: Found ${filesToImport.length} files to import.`);
+
+            // 3. Import Loop
+            const uploadPath = this.configService.get<string>('UPLOAD_FILE_PATH', './uploads');
+            const importTargetDir = path.join(uploadPath, 'imported', taskId);
+
+            if (!fs.existsSync(importTargetDir)) {
+                fs.mkdirSync(importTargetDir, { recursive: true });
+            }
+            this.logger.debug(`Task ${taskId}: Created import target directory ${importTargetDir}.`);
+
+            let successCount = 0;
+            let failCount = 0;
+
+            for (let i = 0; i < filesToImport.length; i++) {
+                const filePath = filesToImport[i];
+                try {
+                    const filename = path.basename(filePath);
+                    // Directory as prefix logic
+                    const dirName = path.basename(path.dirname(filePath));
+                    // Avoid prefix if direct child of source path? No, keep it simple: always prefix with parent folder name
+                    // unless it's the source folder itself? 
+                    // Current plan: "ParentDir - Filename"
+                    // Let's refine: If file is D:/a/b/c.md, and source is D:/a, then dir is b. Title: "b - c.md"
+
+                    let title = filename;
+                    const relativeDir = path.relative(task.sourcePath, path.dirname(filePath));
+                    if (relativeDir && relativeDir !== '.') {
+                        const prefix = relativeDir.replace(/[\\\/]/g, ' - ');
+                        title = `${prefix} - ${filename}`;
+                    }
+
+                    const storedFilename = `imported-${Date.now()}-${Math.random().toString(36).substr(2, 5)}-${filename}`;
+                    const targetPath = path.join(importTargetDir, storedFilename);
+
+                    // Copy file to safe location
+                    fs.copyFileSync(filePath, targetPath);
+
+                    const stats = fs.statSync(targetPath);
+                    const fileInfo = {
+                        filename: storedFilename,
+                        originalname: title,
+                        path: targetPath, // Use the safe copy path
+                        mimetype: 'text/markdown', // Assume markdown/text for now
+                        size: stats.size,
+                    };
+
+                    const indexingConfig = {
+                        chunkSize: task.chunkSize || 500,
+                        chunkOverlap: task.chunkOverlap || 50,
+                        embeddingModelId: task.embeddingModelId,
+                        mode: (task.mode || 'fast') as 'fast' | 'precise',
+                    };
+
+                    // Ingest sequentially
+                    this.logger.log(`Processing file ${i + 1}/${filesToImport.length}: ${fileInfo.originalname}`);
+                    const kb = await this.kbService.createAndIndex(fileInfo, task.userId, {
+                        ...indexingConfig,
+                        waitForCompletion: true // Ensure sequential processing
+                    });
+                    this.logger.log(`File ${i + 1}/${filesToImport.length} processing completed`);
+
+                    // Link to Group
+                    await this.groupService.addFilesToGroup(kb.id, [groupId], task.userId);
+                    this.logger.debug(`Task ${taskId}: Linked KB ${kb.id} to group ${groupId}.`);
+
+                    successCount++;
+                    // Optional: log every single file? Might be too verbose for large courses.
+                    if (successCount % 10 === 0) {
+                        await this.appendLog(taskId, `Imported ${successCount} files...`);
+                    }
+
+                } catch (e) {
+                    failCount++;
+                    await this.appendLog(taskId, `Failed to import ${path.basename(filePath)}: ${e.message}`);
+                }
+            }
+
+            await this.updateStatus(taskId, 'COMPLETED', `Import finished. Success: ${successCount}, Failed: ${failCount}`);
+
+        } catch (error) {
+            await this.updateStatus(taskId, 'FAILED', `Fatal error: ${error.message}`);
+        }
+    }
+
+    private scanDir(directory: string): string[] {
+        let results: string[] = [];
+        const items = fs.readdirSync(directory);
+        for (const item of items) {
+            const fullPath = path.join(directory, item);
+            const stat = fs.statSync(fullPath);
+            if (stat.isDirectory()) {
+                results = results.concat(this.scanDir(fullPath));
+            } else {
+                // Filter for text files and PDFs
+                if (item.match(/\.(md|txt|html|json|pdf)$/i)) {
+                    results.push(fullPath);
+                }
+            }
+        }
+        return results;
+    }
+
+    private async updateStatus(id: string, status: ImportTask['status'], logMessage?: string) {
+        const task = await this.taskRepository.findOne({ where: { id } });
+        if (task) {
+            task.status = status;
+            if (logMessage) {
+                task.logs = (task.logs || '') + `[${new Date().toISOString()}] ${logMessage}\n`;
+            }
+            await this.taskRepository.save(task);
+        }
+    }
+
+    private async appendLog(id: string, message: string) {
+        const task = await this.taskRepository.findOne({ where: { id } });
+        if (task) {
+            task.logs = (task.logs || '') + `[${new Date().toISOString()}] ${message}\n`;
+            await this.taskRepository.save(task);
+        }
+    }
+}

+ 351 - 0
server/src/knowledge-base/chunk-config.service.ts

@@ -0,0 +1,351 @@
+import { Injectable, Logger, BadRequestException } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { ModelConfigService } from '../model-config/model-config.service';
+
+/**
+ * チャンク設定サービス
+ * チャンクパラメータの検証と管理を担当し、モデルの制限や環境変数の設定に適合していることを確認します
+ *
+ * 制限の優先順位:
+ * 1. 環境変数 (MAX_CHUNK_SIZE, MAX_OVERLAP_SIZE)
+ * 2. データベース内のモデル設定 (maxInputTokens, maxBatchSize)
+ * 3. デフォルト値
+ */
+import {
+  DEFAULT_CHUNK_SIZE,
+  MIN_CHUNK_SIZE,
+  DEFAULT_CHUNK_OVERLAP,
+  DEFAULT_MAX_OVERLAP_RATIO,
+  DEFAULT_MAX_BATCH_SIZE,
+  DEFAULT_VECTOR_DIMENSIONS
+} from '../common/constants';
+import { I18nService } from '../i18n/i18n.service';
+
+@Injectable()
+export class ChunkConfigService {
+  private readonly logger = new Logger(ChunkConfigService.name);
+
+  // デフォルト設定
+  private readonly DEFAULTS = {
+    chunkSize: DEFAULT_CHUNK_SIZE,
+    chunkOverlap: DEFAULT_CHUNK_OVERLAP,
+    minChunkSize: MIN_CHUNK_SIZE,
+    maxOverlapRatio: DEFAULT_MAX_OVERLAP_RATIO,  // 重なりはチャンクサイズの50%まで
+    maxBatchSize: DEFAULT_MAX_BATCH_SIZE,    // デフォルトのバッチ制限
+    expectedDimensions: DEFAULT_VECTOR_DIMENSIONS, // デフォルトのベクトル次元
+  };
+
+  // 環境変数で設定された上限(優先的に使用)
+  private readonly envMaxChunkSize: number;
+  private readonly envMaxOverlapSize: number;
+
+  constructor(
+    private configService: ConfigService,
+    private modelConfigService: ModelConfigService,
+    private i18nService: I18nService,
+  ) {
+    // 環境変数からグローバルな上限設定を読み込む
+    this.envMaxChunkSize = parseInt(
+      this.configService.get<string>('MAX_CHUNK_SIZE', '8191')
+    );
+    this.envMaxOverlapSize = parseInt(
+      this.configService.get<string>('MAX_OVERLAP_SIZE', '200')
+    );
+
+    this.logger.log(
+      `環境変数設定の上限: MAX_CHUNK_SIZE=${this.envMaxChunkSize}, MAX_OVERLAP_SIZE=${this.envMaxOverlapSize}`
+    );
+  }
+
+  /**
+   * モデルの制限設定を取得(データベースから読み込み)
+   */
+  async getModelLimits(modelId: string, userId: string): Promise<{
+    maxInputTokens: number;
+    maxBatchSize: number;
+    expectedDimensions: number;
+    providerName: string;
+    isVectorModel: boolean;
+  }> {
+    const modelConfig = await this.modelConfigService.findOne(modelId, userId);
+
+    if (!modelConfig || modelConfig.type !== 'embedding') {
+      throw new BadRequestException(this.i18nService.formatMessage('embeddingModelNotFound', { id: modelId }));
+    }
+
+    // データベースのフィールドから制限を取得し、デフォルト値で補完
+    const maxInputTokens = modelConfig.maxInputTokens || this.envMaxChunkSize;
+    const maxBatchSize = modelConfig.maxBatchSize || this.DEFAULTS.maxBatchSize;
+    const expectedDimensions = modelConfig.dimensions || parseInt(this.configService.get('DEFAULT_VECTOR_DIMENSIONS', String(this.DEFAULTS.expectedDimensions)));
+    const providerName = modelConfig.providerName || '不明';
+    const isVectorModel = modelConfig.isVectorModel || false;
+
+    this.logger.log(
+      this.i18nService.formatMessage('configLoaded', { name: modelConfig.name, id: modelConfig.modelId }) + '\n' +
+      `  - プロバイダー: ${providerName}\n` +
+      `  - Token制限: ${maxInputTokens}\n` +
+      `  - バッチ制限: ${maxBatchSize}\n` +
+      `  - ベクトル次元: ${expectedDimensions}\n` +
+      `  - ベクトルモデルか: ${isVectorModel}`,
+    );
+
+    return {
+      maxInputTokens,
+      maxBatchSize,
+      expectedDimensions,
+      providerName,
+      isVectorModel,
+    };
+  }
+
+  /**
+   * チャンク設定を検証および修正
+   * 優先順位: 環境変数の上限 > モデルの制限 > ユーザー設定
+   */
+  async validateChunkConfig(
+    chunkSize: number,
+    chunkOverlap: number,
+    modelId: string,
+    userId: string,
+  ): Promise<{
+    chunkSize: number;
+    chunkOverlap: number;
+    warnings: string[];
+    effectiveMaxChunkSize: number;
+    effectiveMaxOverlapSize: number;
+  }> {
+    const warnings: string[] = [];
+    const limits = await this.getModelLimits(modelId, userId);
+
+    // 1. 最終的な上限を計算(環境変数とモデル制限の小さい方を選択)
+    const effectiveMaxChunkSize = Math.min(
+      this.envMaxChunkSize,
+      limits.maxInputTokens,
+    );
+
+    const effectiveMaxOverlapSize = Math.min(
+      this.envMaxOverlapSize,
+      Math.floor(effectiveMaxChunkSize * this.DEFAULTS.maxOverlapRatio),
+    );
+
+    // 2. チャンクサイズの上限を検証
+    if (chunkSize > effectiveMaxChunkSize) {
+      const reason =
+        this.envMaxChunkSize < limits.maxInputTokens
+          ? `${this.i18nService.getMessage('environmentLimit')} ${this.envMaxChunkSize}`
+          : `${this.i18nService.getMessage('modelLimit')} ${limits.maxInputTokens}`;
+
+      warnings.push(
+        this.i18nService.formatMessage('chunkOverflow', {
+          size: chunkSize,
+          max: effectiveMaxChunkSize,
+          reason
+        })
+      );
+      chunkSize = effectiveMaxChunkSize;
+    }
+
+    // 3. チャンクサイズの下限を検証
+    if (chunkSize < this.DEFAULTS.minChunkSize) {
+      warnings.push(
+        this.i18nService.formatMessage('chunkUnderflow', {
+          size: chunkSize,
+          min: this.DEFAULTS.minChunkSize
+        })
+      );
+      chunkSize = this.DEFAULTS.minChunkSize;
+    }
+
+    // 4. 重なりサイズの上限を検証(環境変数優先)
+    if (chunkOverlap > effectiveMaxOverlapSize) {
+      warnings.push(
+        this.i18nService.formatMessage('overlapOverflow', {
+          size: chunkOverlap,
+          max: effectiveMaxOverlapSize
+        })
+      );
+      chunkOverlap = effectiveMaxOverlapSize;
+    }
+
+    // 5. 重なりサイズがチャンクサイズの50%を超えないことを検証
+    const maxOverlapByRatio = Math.floor(
+      chunkSize * this.DEFAULTS.maxOverlapRatio,
+    );
+    if (chunkOverlap > maxOverlapByRatio) {
+      warnings.push(
+        this.i18nService.formatMessage('overlapRatioExceeded', {
+          size: chunkOverlap,
+          max: maxOverlapByRatio
+        })
+      );
+      chunkOverlap = maxOverlapByRatio;
+    }
+
+    if (chunkOverlap < 0) {
+      chunkOverlap = 0;
+    }
+
+    // 6. バッチ処理の安全チェックを追加
+    // バッチ処理時、複数のテキストの合計長がモデルの制限を超えないようにする必要があります
+    const safetyMargin = 0.8; // 80% 安全マージン、バッチ処理のためにスペースを確保
+    const safeChunkSize = Math.floor(effectiveMaxChunkSize * safetyMargin);
+
+    if (chunkSize > safeChunkSize) {
+      warnings.push(
+        this.i18nService.formatMessage('batchOverflowWarning', {
+          safeSize: safeChunkSize,
+          size: chunkSize,
+          percent: Math.round(safetyMargin * 100)
+        })
+      );
+    }
+
+    // 7. 推定チャンク数が妥当かチェック
+    const estimatedChunkCount = this.estimateChunkCount(
+      1000000, // 1MB のテキストを想定
+      chunkSize,
+    );
+
+    if (estimatedChunkCount > 50000) {
+      warnings.push(
+        this.i18nService.formatMessage('estimatedChunkCountExcessive', { count: estimatedChunkCount })
+      );
+    }
+
+    return {
+      chunkSize,
+      chunkOverlap,
+      warnings,
+      effectiveMaxChunkSize,
+      effectiveMaxOverlapSize,
+    };
+  }
+
+  /**
+   * 推奨されるバッチサイズを取得
+   */
+  async getRecommendedBatchSize(
+    modelId: string,
+    userId: string,
+    currentBatchSize: number = 100,
+  ): Promise<number> {
+    const limits = await this.getModelLimits(modelId, userId);
+
+    // 設定値とモデル制限の小さい方を選択
+    const recommended = Math.min(
+      currentBatchSize,
+      limits.maxBatchSize,
+      200, // 安全のための上限
+    );
+
+    if (recommended < currentBatchSize) {
+      this.logger.warn(
+        this.i18nService.formatMessage('batchSizeAdjusted', {
+          old: currentBatchSize,
+          new: recommended,
+          limit: limits.maxBatchSize
+        })
+      );
+    }
+
+    return Math.max(10, recommended); // 最低10個
+  }
+
+  /**
+   * チャンク数を推定
+   */
+  estimateChunkCount(textLength: number, chunkSize: number): number {
+    const chunkSizeInChars = chunkSize * 4; // 1 token ≈ 4 chars
+    return Math.ceil(textLength / chunkSizeInChars);
+  }
+
+  /**
+   * ベクトル次元の検証
+   */
+  async validateDimensions(
+    modelId: string,
+    userId: string,
+    actualDimensions: number,
+  ): Promise<boolean> {
+    const limits = await this.getModelLimits(modelId, userId);
+
+    if (actualDimensions !== limits.expectedDimensions) {
+      this.logger.warn(
+        this.i18nService.formatMessage('dimensionMismatch', {
+          id: modelId,
+          expected: limits.expectedDimensions,
+          actual: actualDimensions
+        })
+      );
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * 設定概要を取得(ログ用)
+   */
+  async getConfigSummary(
+    chunkSize: number,
+    chunkOverlap: number,
+    modelId: string,
+    userId: string,
+  ): Promise<string> {
+    const limits = await this.getModelLimits(modelId, userId);
+
+    return [
+      `モデル: ${modelId}`,
+      `チャンクサイズ: ${chunkSize} tokens (制限: ${limits.maxInputTokens})`,
+      `重なりサイズ: ${chunkOverlap} tokens`,
+      `バッチサイズ: ${limits.maxBatchSize}`,
+      `ベクトル次元: ${limits.expectedDimensions}`,
+    ].join(', ');
+  }
+
+  /**
+   * フロントエンド用の設定制限を取得
+   * フロントエンドのスライダーの上限設定に使用
+   */
+  async getFrontendLimits(
+    modelId: string,
+    userId: string,
+  ): Promise<{
+    maxChunkSize: number;
+    maxOverlapSize: number;
+    defaultChunkSize: number;
+    defaultOverlapSize: number;
+    modelInfo: {
+      name: string;
+      maxInputTokens: number;
+      maxBatchSize: number;
+      expectedDimensions: number;
+    };
+  }> {
+    const limits = await this.getModelLimits(modelId, userId);
+
+    // 最終的な上限を計算(環境変数とモデル制限の小さい方を選択)
+    const maxChunkSize = Math.min(this.envMaxChunkSize, limits.maxInputTokens);
+    const maxOverlapSize = Math.min(
+      this.envMaxOverlapSize,
+      Math.floor(maxChunkSize * this.DEFAULTS.maxOverlapRatio),
+    );
+
+    // モデル設定名を取得
+    const modelConfig = await this.modelConfigService.findOne(modelId, userId);
+    const modelName = modelConfig?.name || 'Unknown';
+
+    return {
+      maxChunkSize,
+      maxOverlapSize,
+      defaultChunkSize: Math.min(this.DEFAULTS.chunkSize, maxChunkSize),
+      defaultOverlapSize: Math.min(this.DEFAULTS.chunkOverlap, maxOverlapSize),
+      modelInfo: {
+        name: modelName,
+        maxInputTokens: limits.maxInputTokens,
+        maxBatchSize: limits.maxBatchSize,
+        expectedDimensions: limits.expectedDimensions,
+      },
+    };
+  }
+}

+ 11 - 0
server/src/knowledge-base/dto/create-knowledge-base.dto.ts

@@ -0,0 +1,11 @@
+import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
+
+export class CreateKnowledgeBaseDto {
+  @IsString()
+  @IsNotEmpty()
+  name: string;
+
+  @IsString()
+  @IsOptional()
+  description?: string;
+}

+ 242 - 0
server/src/knowledge-base/embedding.service.ts

@@ -0,0 +1,242 @@
+import { Injectable, Logger } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { ModelConfigService } from '../model-config/model-config.service';
+
+export interface EmbeddingResponse {
+  data: Array<{
+    embedding: number[];
+    index: number;
+  }>;
+  model: string;
+  usage: {
+    prompt_tokens: number;
+    total_tokens: number;
+  };
+}
+
+@Injectable()
+export class EmbeddingService {
+  private readonly logger = new Logger(EmbeddingService.name);
+  private readonly defaultDimensions: number;
+
+  constructor(
+    private modelConfigService: ModelConfigService,
+    private configService: ConfigService,
+  ) {
+    this.defaultDimensions = parseInt(
+      this.configService.get<string>('DEFAULT_VECTOR_DIMENSIONS', '2560'),
+    );
+    this.logger.log(`デフォルトのベクトル次元が ${this.defaultDimensions} に設定されました`);
+  }
+
+  async getEmbeddings(
+    texts: string[],
+    userId: string,
+    embeddingModelConfigId: string,
+  ): Promise<number[][]> {
+    this.logger.log(`${texts.length} 個のテキストに対して埋め込みベクトルを生成しています`);
+
+    const modelConfig = await this.modelConfigService.findOne(
+      embeddingModelConfigId,
+      userId,
+    );
+    if (!modelConfig || modelConfig.type !== 'embedding') {
+      throw new Error(`埋め込みモデル設定 ${embeddingModelConfigId} が見つかりません`);
+    }
+
+    if (modelConfig.isEnabled === false) {
+      throw new Error(`モデル ${modelConfig.name} は無効化されているため、埋め込みベクトルを生成できません`);
+    }
+
+    if (!modelConfig.apiKey) {
+      throw new Error(`モデル ${modelConfig.name} に API キーが設定されていません`);
+    }
+
+    if (!modelConfig.baseUrl) {
+      throw new Error(`モデル ${modelConfig.name} に baseUrl が設定されていません`);
+    }
+
+    // モデル名に基づいて最大バッチサイズを決定
+    const maxBatchSize = this.getMaxBatchSizeForModel(modelConfig.modelId, modelConfig.maxBatchSize);
+
+    // バッチサイズが制限を超える場合は分割して処理
+    if (texts.length > maxBatchSize) {
+      this.logger.log(
+        `テキスト数 ${texts.length} がモデルのバッチ制限 ${maxBatchSize} を超えているため、分割処理します`
+      );
+
+      const allEmbeddings: number[][] = [];
+
+      for (let i = 0; i < texts.length; i += maxBatchSize) {
+        const batch = texts.slice(i, i + maxBatchSize);
+        const batchEmbeddings = await this.getEmbeddingsForBatch(
+          batch,
+          userId,
+          modelConfig,
+          maxBatchSize
+        );
+
+        allEmbeddings.push(...batchEmbeddings);
+
+        // APIレート制限対策のため、短い間隔で待機
+        if (i + maxBatchSize < texts.length) {
+          await new Promise(resolve => setTimeout(resolve, 100)); // 100ms待機
+        }
+      }
+
+      return allEmbeddings;
+    } else {
+      // 通常処理(バッチサイズ以内)
+      return await this.getEmbeddingsForBatch(
+        texts,
+        userId,
+        modelConfig,
+        maxBatchSize
+      );
+    }
+  }
+
+  /**
+   * モデルIDに基づいて最大バッチサイズを決定
+   */
+  private getMaxBatchSizeForModel(modelId: string, configuredMaxBatchSize?: number): number {
+    // モデル固有のバッチサイズ制限
+    if (modelId.includes('text-embedding-004') || modelId.includes('text-embedding-v4') ||
+        modelId.includes('text-embedding-ada-002')) {
+      return Math.min(10, configuredMaxBatchSize || 100); // Googleの場合は10を上限
+    } else if (modelId.includes('text-embedding-3') || modelId.includes('text-embedding-003')) {
+      return Math.min(2048, configuredMaxBatchSize || 2048); // OpenAI v3は2048が上限
+    } else {
+      // デフォルトでは設定された最大バッチサイズか100の小さい方
+      return Math.min(configuredMaxBatchSize || 100, 100);
+    }
+  }
+
+  /**
+   * 単一バッチの埋め込み処理
+   */
+  private async getEmbeddingsForBatch(
+    texts: string[],
+    userId: string,
+    modelConfig: any,
+    maxBatchSize: number,
+  ): Promise<number[][]> {
+    const apiUrl = modelConfig.baseUrl.endsWith('/embeddings')
+      ? modelConfig.baseUrl
+      : `${modelConfig.baseUrl}/embeddings`;
+
+    let lastError;
+    const MAX_RETRIES = 3;
+
+    for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
+      try {
+        const controller = new AbortController();
+        const timeoutId = setTimeout(() => {
+          controller.abort();
+          this.logger.error(`Embedding API timeout after 60s: ${apiUrl}`);
+        }, 60000); // 60s timeout
+
+        this.logger.log(`[モデル呼び出し] タイプ: Embedding, モデル: ${modelConfig.name} (${modelConfig.modelId}), ユーザー: ${userId}, テキスト数: ${texts.length}`);
+        this.logger.log(`埋め込み API を呼び出し中 (試行 ${attempt}/${MAX_RETRIES}): ${apiUrl}`);
+
+        let response;
+        try {
+          response = await fetch(apiUrl, {
+            method: 'POST',
+            headers: {
+              'Content-Type': 'application/json',
+              Authorization: `Bearer ${modelConfig.apiKey}`,
+            },
+            body: JSON.stringify({
+              encoding_format: 'float',
+              input: texts,
+              model: modelConfig.modelId,
+            }),
+            signal: controller.signal,
+          });
+        } finally {
+          clearTimeout(timeoutId);
+        }
+
+        if (!response.ok) {
+          const errorText = await response.text();
+
+          // バッチサイズ制限エラーを検出
+          if (errorText.includes('batch size is invalid') || errorText.includes('batch_size') ||
+              errorText.includes('invalid') || errorText.includes('larger than')) {
+            this.logger.warn(
+              `バッチサイズ制限エラーが検出されました。バッチサイズを半分に分割して再試行します: ${maxBatchSize} -> ${Math.floor(maxBatchSize/2)}`
+            );
+
+            // バッチをさらに小さな単位に分割して再試行
+            if (texts.length > 1) {
+              const midPoint = Math.floor(texts.length / 2);
+              const firstHalf = texts.slice(0, midPoint);
+              const secondHalf = texts.slice(midPoint);
+
+              const firstResult = await this.getEmbeddingsForBatch(firstHalf, userId, modelConfig, Math.floor(maxBatchSize/2));
+              const secondResult = await this.getEmbeddingsForBatch(secondHalf, userId, modelConfig, Math.floor(maxBatchSize/2));
+
+              return [...firstResult, ...secondResult];
+            }
+          }
+
+          // コンテキスト長の過剰エラーを検出
+          if (errorText.includes('context length') || errorText.includes('exceeds')) {
+            const avgLength = texts.reduce((s, t) => s + t.length, 0) / texts.length;
+            const totalLength = texts.reduce((s, t) => s + t.length, 0);
+            this.logger.error(
+              `テキスト長が制限を超過しました: 入力 ${texts.length} 個のテキスト、` +
+              `総計 ${totalLength} 文字、平均 ${Math.round(avgLength)} 文字、` +
+              `モデル制限: ${modelConfig.maxInputTokens || 8192} tokens`
+            );
+            throw new Error(
+              `テキスト長がモデルの制限を超えています。` +
+              `現在: ${texts.length} 個のテキストで計 ${totalLength} 文字、` +
+              `モデル制限: ${modelConfig.maxInputTokens || 8192} tokens。` +
+              `アドバイス: チャンクサイズまたはバッチサイズを小さくしてください`
+            );
+          }
+
+          // 429 (Too Many Requests) または 5xx (Server Error) の場合は再試行
+          if (response.status === 429 || response.status >= 500) {
+            this.logger.warn(`埋め込み API で一時的なエラーが発生しました (${response.status}): ${errorText}`);
+            throw new Error(`API Error ${response.status}: ${errorText}`);
+          }
+
+          this.logger.error(`埋め込み API エラーの詳細: ${errorText}`);
+          this.logger.error(`リクエストパラメータ: model=${modelConfig.modelId}, inputLength=${texts[0]?.length}`);
+          throw new Error(`埋め込み API の呼び出しに失敗しました: ${response.statusText} - ${errorText}`);
+        }
+
+        const data: EmbeddingResponse = await response.json();
+        const embeddings = data.data.map((item) => item.embedding);
+
+        // 実際のレスポンスから次元を取得
+        const actualDimensions = embeddings[0]?.length || this.defaultDimensions;
+        this.logger.log(
+          `${modelConfig.name} から ${embeddings.length} 個の埋め込みベクトルを取得しました。次元: ${actualDimensions}`,
+        );
+
+        return embeddings;
+      } catch (error) {
+        lastError = error;
+
+        // 最後のアテンプトでなく、エラーが一時的と思われる場合(または堅牢性のために全て)は、待機後に再試行
+        if (attempt < MAX_RETRIES) {
+          const delay = Math.pow(2, attempt - 1) * 1000; // 1s, 2s, 4s
+          this.logger.warn(`埋め込みリクエストが失敗しました。${delay}ms 後に再試行します: ${error.message}`);
+          await new Promise(resolve => setTimeout(resolve, delay));
+          continue;
+        }
+      }
+    }
+
+    throw lastError;
+  }
+
+  private getEstimatedDimensions(modelId: string): number {
+    // 使用环境变量的默认维度
+    return this.defaultDimensions;
+  }
+}

+ 304 - 0
server/src/knowledge-base/knowledge-base.controller.ts

@@ -0,0 +1,304 @@
+import {
+  Body,
+  Controller,
+  Delete,
+  Get,
+  Param,
+  Post,
+  Query,
+  Request,
+  UseGuards,
+  Res,
+  NotFoundException,
+  InternalServerErrorException,
+} from '@nestjs/common';
+import { Response } from 'express';
+import * as path from 'path';
+import { Logger } from '@nestjs/common';
+import { KnowledgeBaseService } from './knowledge-base.service';
+import { JwtAuthGuard } from '../auth/jwt-auth.guard';
+import { AdminGuard } from '../auth/admin.guard';
+import { Public } from '../auth/public.decorator';
+import { KnowledgeBase } from './knowledge-base.entity';
+import { ChunkConfigService } from './chunk-config.service';
+import { KnowledgeGroupService } from '../knowledge-group/knowledge-group.service';
+
+@Controller('knowledge-bases')
+@UseGuards(JwtAuthGuard)
+export class KnowledgeBaseController {
+  private readonly logger = new Logger(KnowledgeBaseController.name);
+
+  constructor(
+    private readonly knowledgeBaseService: KnowledgeBaseService,
+    private readonly chunkConfigService: ChunkConfigService,
+    private readonly knowledgeGroupService: KnowledgeGroupService,
+  ) { }
+
+  @Get()
+  @UseGuards(JwtAuthGuard)
+  async findAll(@Request() req): Promise<KnowledgeBase[]> {
+    return this.knowledgeBaseService.findAll(req.user.id);
+  }
+
+  @Delete('clear')
+  @UseGuards(AdminGuard)  // Only admin can clear all knowledge base
+  async clearAll(@Request() req): Promise<{ message: string }> {
+    await this.knowledgeBaseService.clearAll(req.user.id);
+    return { message: 'ナレッジベースが空になりました' };
+  }
+
+  @Post('search')
+  @UseGuards(JwtAuthGuard)
+  async search(@Request() req, @Body() body: { query: string; topK?: number }) {
+    return this.knowledgeBaseService.searchKnowledge(
+      req.user.id,
+      body.query,
+      body.topK || 5,
+    );
+  }
+
+  @Post('rag-search')
+  @UseGuards(JwtAuthGuard)
+  async ragSearch(
+    @Request() req,
+    @Body() body: { query: string; settings: any },
+  ) {
+    return this.knowledgeBaseService.ragSearch(
+      req.user.id,
+      body.query,
+      body.settings,
+    );
+  }
+
+  @Delete(':id')
+  @UseGuards(AdminGuard)  // Only admin can delete files
+  async deleteFile(
+    @Request() req,
+    @Param('id') fileId: string,
+  ): Promise<{ message: string }> {
+    await this.knowledgeBaseService.deleteFile(fileId, req.user.id);
+    return { message: 'ファイルが削除されました' };
+  }
+
+  @Post(':id/retry')
+  @UseGuards(AdminGuard)  // Only admin can retry files
+  async retryFile(
+    @Request() req,
+    @Param('id') fileId: string,
+  ): Promise<KnowledgeBase> {
+    return this.knowledgeBaseService.retryFailedFile(fileId, req.user.id);
+  }
+
+  @Get(':id/chunks')
+  @UseGuards(JwtAuthGuard)
+  async getFileChunks(
+    @Request() req,
+    @Param('id') fileId: string,
+  ) {
+    return this.knowledgeBaseService.getFileChunks(fileId, req.user.id);
+  }
+
+
+  /**
+   * チャンク設定の制限を取得(フロントエンドのスライダー設定用)
+   * クエリパラメータ: embeddingModelId - 埋め込みモデルID
+   */
+  @Get('chunk-config/limits')
+  @UseGuards(JwtAuthGuard)
+  async getChunkConfigLimits(
+    @Request() req,
+    @Query('embeddingModelId') embeddingModelId: string,
+  ) {
+    if (!embeddingModelId) {
+      return {
+        maxChunkSize: parseInt(process.env.MAX_CHUNK_SIZE || '8191'),
+        maxOverlapSize: parseInt(process.env.MAX_OVERLAP_SIZE || '200'),
+        defaultChunkSize: 200,
+        defaultOverlapSize: 40,
+        modelInfo: {
+          name: 'モデル未選択',
+          maxInputTokens: parseInt(process.env.MAX_CHUNK_SIZE || '8191'),
+          maxBatchSize: 2048,
+          expectedDimensions: parseInt(process.env.DEFAULT_VECTOR_DIMENSIONS || '2560'),
+        },
+      };
+    }
+
+    return await this.chunkConfigService.getFrontendLimits(
+      embeddingModelId,
+      req.user.id,
+    );
+  }
+
+  // ファイルグループ管理 - 添加管理员权限
+  @Post(':id/groups')
+  @UseGuards(AdminGuard)  // Only admin can add files to groups
+  async addFileToGroups(
+    @Param('id') fileId: string,
+    @Body() body: { groupIds: string[] },
+    @Request() req,
+  ) {
+    await this.knowledgeGroupService.addFilesToGroup(
+      fileId,
+      body.groupIds,
+      req.user.id,
+    );
+    return { message: 'ファイルグループが更新されました' };
+  }
+
+  @Delete(':id/groups/:groupId')
+  @UseGuards(AdminGuard)  // Only admin can remove files from groups
+  async removeFileFromGroup(
+    @Param('id') fileId: string,
+    @Param('groupId') groupId: string,
+    @Request() req,
+  ) {
+    await this.knowledgeGroupService.removeFileFromGroup(
+      fileId,
+      groupId,
+      req.user.id,
+    );
+    return { message: 'ファイルがグループから削除されました' };
+  }
+
+  // PDF プレビュー - 公開アクセス
+  @Public()
+  @Get(':id/pdf')
+  async getPDFPreview(
+    @Param('id') fileId: string,
+    @Query('token') token: string,
+    @Res() res: Response,
+  ) {
+    try {
+      if (!token) {
+        throw new NotFoundException('アクセス不許可:トークンがありません');
+      }
+
+      const jwt = await import('jsonwebtoken');
+      const secret = process.env.JWT_SECRET;
+      if (!secret) {
+        throw new InternalServerErrorException('JWT_SECRET environment variable is required but not set');
+      }
+
+      let decoded;
+      try {
+        decoded = jwt.verify(token, secret) as any;
+      } catch {
+        throw new NotFoundException('無効なトークンです');
+      }
+
+      if (decoded.type !== 'pdf-access' || decoded.fileId !== fileId) {
+        throw new NotFoundException('無効なトークンです');
+      }
+
+      const pdfPath = await this.knowledgeBaseService.ensurePDFExists(
+        fileId,
+        decoded.userId,
+      );
+
+      const fs = await import('fs');
+      const path = await import('path');
+
+      if (!fs.existsSync(pdfPath)) {
+        throw new NotFoundException('PDF ファイルが見つかりません');
+      }
+
+      const stat = fs.statSync(pdfPath);
+      const fileName = path.basename(pdfPath);
+
+      if (stat.size === 0) {
+        this.logger.warn(`PDF file is empty: ${pdfPath}`);
+        try {
+          fs.unlinkSync(pdfPath); // 空のファイルを削除
+        } catch (e) { }
+        throw new NotFoundException('PDF ファイルが空です。変換に失敗した可能性があります');
+      }
+
+      res.setHeader('Content-Type', 'application/pdf');
+      res.setHeader('Content-Length', stat.size);
+
+      const stream = fs.createReadStream(pdfPath);
+      stream.pipe(res);
+    } catch (error) {
+      if (error instanceof NotFoundException) {
+        throw error;
+      }
+      this.logger.error(`PDF preview error: ${error.message}`);
+      throw new NotFoundException('PDF ファイルが存在しないか、変換に失敗しました');
+    }
+  }
+
+  // PDF プレビューアドレスを取得
+  @Get(':id/pdf-url')
+  @UseGuards(JwtAuthGuard)  // Allow any authenticated user to access their own PDF
+  async getPDFUrl(
+    @Param('id') fileId: string,
+    @Query('force') force: string,
+    @Request() req,
+  ) {
+    try {
+      // PDF 変換をトリガー
+      await this.knowledgeBaseService.ensurePDFExists(fileId, req.user.id, force === 'true');
+
+      // 一時的なアクセストークンを生成
+      const jwt = await import('jsonwebtoken');
+
+      const secret = process.env.JWT_SECRET;
+      if (!secret) {
+        throw new InternalServerErrorException('JWT_SECRET environment variable is required but not set');
+      }
+
+      const token = jwt.sign(
+        { fileId, userId: req.user.id, type: 'pdf-access' },
+        secret,
+        { expiresIn: '1h' }
+      );
+
+      return {
+        url: `/api/knowledge-bases/${fileId}/pdf?token=${token}`
+      };
+    } catch (error) {
+      if (error.message.includes('LibreOffice')) {
+        throw new Error(`PDF サービスを利用できません: ${error.message}`);
+      }
+      throw error;
+    }
+  }
+
+  @Get(':id/pdf-status')
+  @UseGuards(JwtAuthGuard)  // Allow any authenticated user to check their own PDF status
+  async getPDFStatus(
+    @Param('id') fileId: string,
+    @Request() req,
+  ) {
+    return await this.knowledgeBaseService.getPDFStatus(fileId, req.user.id);
+  }
+
+  // PDF の特定ページの画像を取得
+  @Get(':id/page/:index')
+  @UseGuards(JwtAuthGuard)  // Allow any authenticated user to access their own PDF page images
+  async getPageImage(
+    @Param('id') fileId: string,
+    @Param('index') index: number,
+    @Request() req,
+    @Res() res: Response,
+  ) {
+    try {
+      const imagePath = await this.knowledgeBaseService.getPageAsImage(
+        fileId,
+        Number(index),
+        req.user.id,
+      );
+
+      const fs = await import('fs');
+      if (!fs.existsSync(imagePath)) {
+        throw new NotFoundException('ページ画像が見つかりません');
+      }
+
+      res.sendFile(path.resolve(imagePath));
+    } catch (error) {
+      this.logger.error(`PDF ページの画像取得に失敗しました: ${error.message}`);
+      throw new NotFoundException('PDF ページの画像を取得できませんでした');
+    }
+  }
+}

+ 89 - 0
server/src/knowledge-base/knowledge-base.entity.ts

@@ -0,0 +1,89 @@
+import {
+  Column,
+  CreateDateColumn,
+  Entity,
+  PrimaryGeneratedColumn,
+  UpdateDateColumn,
+  ManyToMany,
+} from 'typeorm';
+import { KnowledgeGroup } from '../knowledge-group/knowledge-group.entity';
+
+export enum FileStatus {
+  PENDING = 'pending',
+  INDEXING = 'indexing',
+  EXTRACTED = 'extracted', // テキスト抽出が完了し、データベースに保存されました
+  VECTORIZED = 'vectorized', // ベクトル化が完了し、ES にインデックスされました
+  FAILED = 'failed',
+}
+
+export enum ProcessingMode {
+  FAST = 'fast',      // 高速モード - Tika を使用
+  PRECISE = 'precise', // 精密モード - Vision Pipeline を使用
+}
+
+@Entity('knowledge_bases')
+export class KnowledgeBase {
+  @PrimaryGeneratedColumn('uuid')
+  id: string;
+
+  @Column({ nullable: true })
+  title: string;
+
+  @Column({ name: 'original_name' })
+  originalName: string;
+
+  @Column({ name: 'storage_path' })
+  storagePath: string;
+
+  @Column({ type: 'integer', default: 0 })
+  size: number;
+
+  @Column({ length: 100, nullable: true })
+  mimetype: string;
+
+  @Column({
+    type: 'simple-enum',
+    enum: FileStatus,
+    default: FileStatus.PENDING,
+  })
+  status: FileStatus;
+
+  @Column({ name: 'user_id', nullable: true }) // 暫定的に空を許可(デバッグ用)、将来的には必須にすべき
+  userId: string;
+
+  @Column({ type: 'text', nullable: true })
+  content: string; // Tika で抽出されたテキスト内容を保存
+
+  // インデックス設定パラメータ
+  @Column({ name: 'chunk_size', type: 'integer', default: 1000 })
+  chunkSize: number;
+
+  @Column({ name: 'chunk_overlap', type: 'integer', default: 200 })
+  chunkOverlap: number;
+
+  @Column({ name: 'embedding_model_id', nullable: true })
+  embeddingModelId: string;
+
+  @Column({
+    type: 'simple-enum',
+    enum: ProcessingMode,
+    default: ProcessingMode.FAST,
+    name: 'processing_mode',
+  })
+  processingMode: ProcessingMode;
+
+  @Column({ type: 'json', nullable: true })
+  metadata: any; // 追加のメタデータを保存(画像の説明、信頼度など)
+
+  @Column({ name: 'pdf_path', nullable: true })
+  pdfPath: string; // PDF ファイルパス(プレビュー用)
+
+  @ManyToMany(() => KnowledgeGroup, (group) => group.knowledgeBases)
+  groups: KnowledgeGroup[];
+
+  @CreateDateColumn({ name: 'created_at' })
+  createdAt: Date;
+
+  @UpdateDateColumn({ name: 'updated_at' })
+  updatedAt: Date;
+}

Some files were not shown because too many files changed in this diff