diff --git a/.env.example b/.env.example index 4fd526f569..e746737ea4 100644 --- a/.env.example +++ b/.env.example @@ -47,6 +47,10 @@ TRUST_PROXY=1 # password policies. # MIN_PASSWORD_LENGTH=8 +# When enabled, the app will continue running after encountering uncaught exceptions +# instead of exiting the process. Not recommended for production unless necessary. +# CONTINUE_ON_UNCAUGHT_EXCEPTION=false + #===============# # JSON Logging # #===============# @@ -61,6 +65,9 @@ CONSOLE_JSON=false DEBUG_LOGGING=true DEBUG_CONSOLE=false +# Enable memory diagnostics (logs heap/RSS snapshots every 60s, auto-enabled with --inspect) +# MEM_DIAG=true + #=============# # Permissions # #=============# @@ -131,7 +138,7 @@ PROXY= #============# ANTHROPIC_API_KEY=user_provided -# ANTHROPIC_MODELS=claude-opus-4-6,claude-opus-4-20250514,claude-sonnet-4-20250514,claude-3-7-sonnet-20250219,claude-3-5-sonnet-20241022,claude-3-5-haiku-20241022,claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307 +# ANTHROPIC_MODELS=claude-sonnet-4-6,claude-opus-4-6,claude-opus-4-20250514,claude-sonnet-4-20250514,claude-3-7-sonnet-20250219,claude-3-5-sonnet-20241022,claude-3-5-haiku-20241022,claude-3-opus-20240229,claude-3-sonnet-20240229,claude-3-haiku-20240307 # ANTHROPIC_REVERSE_PROXY= # Set to true to use Anthropic models through Google Vertex AI instead of direct API @@ -166,8 +173,8 @@ ANTHROPIC_API_KEY=user_provided # BEDROCK_AWS_SESSION_TOKEN=someSessionToken # Note: This example list is not meant to be exhaustive. If omitted, all known, supported model IDs will be included for you. -# BEDROCK_AWS_MODELS=anthropic.claude-opus-4-6-v1,anthropic.claude-3-5-sonnet-20240620-v1:0,meta.llama3-1-8b-instruct-v1:0 -# Cross-region inference model IDs: us.anthropic.claude-opus-4-6-v1,global.anthropic.claude-opus-4-6-v1 +# BEDROCK_AWS_MODELS=anthropic.claude-sonnet-4-6,anthropic.claude-opus-4-6-v1,anthropic.claude-3-5-sonnet-20240620-v1:0,meta.llama3-1-8b-instruct-v1:0 +# Cross-region inference model IDs: us.anthropic.claude-sonnet-4-6,us.anthropic.claude-opus-4-6-v1,global.anthropic.claude-opus-4-6-v1 # See all Bedrock model IDs here: https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html#model-ids-arns @@ -189,10 +196,10 @@ GOOGLE_KEY=user_provided # GOOGLE_AUTH_HEADER=true # Gemini API (AI Studio) -# GOOGLE_MODELS=gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash,gemini-2.0-flash-lite +# GOOGLE_MODELS=gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash,gemini-2.0-flash-lite # Vertex AI -# GOOGLE_MODELS=gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash-001,gemini-2.0-flash-lite-001 +# GOOGLE_MODELS=gemini-3.1-pro-preview,gemini-3.1-pro-preview-customtools,gemini-3.1-flash-lite-preview,gemini-2.5-pro,gemini-2.5-flash,gemini-2.5-flash-lite,gemini-2.0-flash-001,gemini-2.0-flash-lite-001 # GOOGLE_TITLE_MODEL=gemini-2.0-flash-lite-001 @@ -239,10 +246,6 @@ GOOGLE_KEY=user_provided # Option A: Use dedicated Gemini API key for image generation # GEMINI_API_KEY=your-gemini-api-key -# Option B: Use Vertex AI (no API key needed, uses service account) -# Set this to enable Vertex AI and allow tool without requiring API keys -# GEMINI_VERTEX_ENABLED=true - # Vertex AI model for image generation (defaults to gemini-2.5-flash-image) # GEMINI_IMAGE_MODEL=gemini-2.5-flash-image @@ -510,6 +513,9 @@ OPENID_ADMIN_ROLE_TOKEN_KIND= OPENID_USERNAME_CLAIM= # Set to determine which user info property returned from OpenID Provider to store as the User's name OPENID_NAME_CLAIM= +# Set to determine which user info claim to use as the email/identifier for user matching (e.g., "upn" for Entra ID) +# When not set, defaults to: email -> preferred_username -> upn +OPENID_EMAIL_CLAIM= # Optional audience parameter for OpenID authorization requests OPENID_AUDIENCE= @@ -654,6 +660,9 @@ AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_REGION= AWS_BUCKET_NAME= +# Required for path-style S3-compatible providers (MinIO, Hetzner, Backblaze B2, etc.) +# that don't support virtual-hosted-style URLs (bucket.endpoint). Not needed for AWS S3. +# AWS_FORCE_PATH_STYLE=false #========================# # Azure Blob Storage # @@ -668,7 +677,8 @@ AZURE_CONTAINER_NAME=files #========================# ALLOW_SHARED_LINKS=true -ALLOW_SHARED_LINKS_PUBLIC=true +# Allows unauthenticated access to shared links. Defaults to false (auth required) if not set. +ALLOW_SHARED_LINKS_PUBLIC=false #==============================# # Static File Cache Control # @@ -840,3 +850,24 @@ OPENWEATHER_API_KEY= # Skip code challenge method validation (e.g., for AWS Cognito that supports S256 but doesn't advertise it) # When set to true, forces S256 code challenge even if not advertised in .well-known/openid-configuration # MCP_SKIP_CODE_CHALLENGE_CHECK=false + +# Circuit breaker: max connect/disconnect cycles before tripping (per server) +# MCP_CB_MAX_CYCLES=7 + +# Circuit breaker: sliding window (ms) for counting cycles +# MCP_CB_CYCLE_WINDOW_MS=45000 + +# Circuit breaker: cooldown (ms) after the cycle breaker trips +# MCP_CB_CYCLE_COOLDOWN_MS=15000 + +# Circuit breaker: max consecutive failed connection rounds before backoff +# MCP_CB_MAX_FAILED_ROUNDS=3 + +# Circuit breaker: sliding window (ms) for counting failed rounds +# MCP_CB_FAILED_WINDOW_MS=120000 + +# Circuit breaker: base backoff (ms) after failed round threshold is reached +# MCP_CB_BASE_BACKOFF_MS=30000 + +# Circuit breaker: max backoff cap (ms) for exponential backoff +# MCP_CB_MAX_BACKOFF_MS=300000 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ad0a75ab9b..ae9e6d8e4b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -26,18 +26,14 @@ Project maintainers have the right and responsibility to remove, edit, or reject ## 1. Development Setup -1. Use Node.JS 20.x. -2. Install typescript globally: `npm i -g typescript`. -3. Run `npm ci` to install dependencies. -4. Build the data provider: `npm run build:data-provider`. -5. Build data schemas: `npm run build:data-schemas`. -6. Build API methods: `npm run build:api`. -7. Setup and run unit tests: +1. Use Node.js v20.19.0+ or ^22.12.0 or >= 23.0.0. +2. Run `npm run smart-reinstall` to install dependencies (uses Turborepo). Use `npm run reinstall` for a clean install, or `npm ci` for a fresh lockfile-based install. +3. Build all compiled code: `npm run build`. +4. Setup and run unit tests: - Copy `.env.test`: `cp api/test/.env.test.example api/test/.env.test`. - Run backend unit tests: `npm run test:api`. - Run frontend unit tests: `npm run test:client`. -8. Setup and run integration tests: - - Build client: `cd client && npm run build`. +5. Setup and run integration tests: - Create `.env`: `cp .env.example .env`. - Install [MongoDB Community Edition](https://www.mongodb.com/docs/manual/administration/install-community/), ensure that `mongosh` connects to your local instance. - Run: `npx install playwright`, then `npx playwright install`. @@ -48,11 +44,11 @@ Project maintainers have the right and responsibility to remove, edit, or reject ## 2. Development Notes 1. Before starting work, make sure your main branch has the latest commits with `npm run update`. -3. Run linting command to find errors: `npm run lint`. Alternatively, ensure husky pre-commit checks are functioning. +2. Run linting command to find errors: `npm run lint`. Alternatively, ensure husky pre-commit checks are functioning. 3. After your changes, reinstall packages in your current branch using `npm run reinstall` and ensure everything still works. - Restart the ESLint server ("ESLint: Restart ESLint Server" in VS Code command bar) and your IDE after reinstalling or updating. 4. Clear web app localStorage and cookies before and after changes. -5. For frontend changes, compile typescript before and after changes to check for introduced errors: `cd client && npm run build`. +5. To check for introduced errors, build all compiled code: `npm run build`. 6. Run backend unit tests: `npm run test:api`. 7. Run frontend unit tests: `npm run test:client`. 8. Run integration tests: `npm run e2e`. @@ -118,50 +114,45 @@ Apply the following naming conventions to branches, labels, and other Git-relate - **JS/TS:** Directories and file names: Descriptive and camelCase. First letter uppercased for React files (e.g., `helperFunction.ts, ReactComponent.tsx`). - **Docs:** Directories and file names: Descriptive and snake_case (e.g., `config_files.md`). -## 7. TypeScript Conversion +## 7. Coding Standards + +For detailed coding conventions, workspace boundaries, and architecture guidance, refer to the [`AGENTS.md`](../AGENTS.md) file at the project root. It covers code style, type safety, import ordering, iteration/performance expectations, frontend rules, testing, and development commands. + +## 8. TypeScript Conversion 1. **Original State**: The project was initially developed entirely in JavaScript (JS). -2. **Frontend Transition**: - - We are in the process of transitioning the frontend from JS to TypeScript (TS). - - The transition is nearing completion. - - This conversion is feasible due to React's capability to intermix JS and TS prior to code compilation. It's standard practice to compile/bundle the code in such scenarios. +2. **Frontend**: Fully transitioned to TypeScript. -3. **Backend Considerations**: - - Transitioning the backend to TypeScript would be a more intricate process, especially for an established Express.js server. - - - **Options for Transition**: - - **Single Phase Overhaul**: This involves converting the entire backend to TypeScript in one go. It's the most straightforward approach but can be disruptive, especially for larger codebases. - - - **Incremental Transition**: Convert parts of the backend progressively. This can be done by: - - Maintaining a separate directory for TypeScript files. - - Gradually migrating and testing individual modules or routes. - - Using a build tool like `tsc` to compile TypeScript files independently until the entire transition is complete. - - - **Compilation Considerations**: - - Introducing a compilation step for the server is an option. This would involve using tools like `ts-node` for development and `tsc` for production builds. - - However, this is not a conventional approach for Express.js servers and could introduce added complexity, especially in terms of build and deployment processes. - - - **Current Stance**: At present, this backend transition is of lower priority and might not be pursued. +3. **Backend**: + - The legacy Express.js server remains in `/api` as JavaScript. + - All new backend code is written in TypeScript under `/packages/api`, which is compiled and consumed by `/api`. + - Shared database logic lives in `/packages/data-schemas` (TypeScript). + - Shared frontend/backend API types and services live in `/packages/data-provider` (TypeScript). + - Minimize direct changes to `/api`; prefer adding TypeScript code to `/packages/api` and importing it. -## 8. Module Import Conventions +## 9. Module Import Conventions -- `npm` packages first, - - from longest line (top) to shortest (bottom) +Imports are organized into three sections (in order): -- Followed by typescript types (pertains to data-provider and client workspaces) - - longest line (top) to shortest (bottom) - - types from package come first +1. **Package imports** — sorted from shortest to longest line length. + - `react` is always the first import. + - Multi-line (stacked) imports count their total character length across all lines for sorting. -- Lastly, local imports - - longest line (top) to shortest (bottom) - - imports with alias `~` treated the same as relative import with respect to line length +2. **`import type` imports** — sorted from longest to shortest line length. + - Package type imports come first, then local type imports. + - Line length sorting resets between the package and local sub-groups. + +3. **Local/project imports** — sorted from longest to shortest line length. + - Multi-line (stacked) imports count their total character length across all lines for sorting. + - Imports with alias `~` are treated the same as relative imports with respect to line length. + +- Consolidate value imports from the same module as much as possible. +- Always use standalone `import type { ... }` for type imports; never use inline `type` keyword inside value imports (e.g., `import { Foo, type Bar }` is wrong). **Note:** ESLint will automatically enforce these import conventions when you run `npm run lint --fix` or through pre-commit hooks. ---- - -Please ensure that you adapt this summary to fit the specific context and nuances of your project. +For the full set of coding standards, see [`AGENTS.md`](../AGENTS.md). --- diff --git a/.github/workflows/backend-review.yml b/.github/workflows/backend-review.yml index 2379b8fee7..038c90627e 100644 --- a/.github/workflows/backend-review.yml +++ b/.github/workflows/backend-review.yml @@ -9,48 +9,145 @@ on: paths: - 'api/**' - 'packages/**' + +env: + NODE_ENV: CI + NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}' + jobs: - tests_Backend: - name: Run Backend unit tests - timeout-minutes: 60 + build: + name: Build packages runs-on: ubuntu-latest - env: - MONGO_URI: ${{ secrets.MONGO_URI }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - JWT_SECRET: ${{ secrets.JWT_SECRET }} - CREDS_KEY: ${{ secrets.CREDS_KEY }} - CREDS_IV: ${{ secrets.CREDS_IV }} - BAN_VIOLATIONS: ${{ secrets.BAN_VIOLATIONS }} - BAN_DURATION: ${{ secrets.BAN_DURATION }} - BAN_INTERVAL: ${{ secrets.BAN_INTERVAL }} - NODE_ENV: CI - NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}' + timeout-minutes: 15 steps: - uses: actions/checkout@v4 - - name: Use Node.js 20.x + + - name: Use Node.js 20.19 uses: actions/setup-node@v4 with: - node-version: 20 - cache: 'npm' + node-version: '20.19' + + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + api/node_modules + packages/api/node_modules + packages/data-provider/node_modules + packages/data-schemas/node_modules + key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }} - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' run: npm ci - - name: Install Data Provider Package + - name: Restore data-provider build cache + id: cache-data-provider + uses: actions/cache@v4 + with: + path: packages/data-provider/dist + key: build-data-provider-${{ runner.os }}-${{ hashFiles('packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }} + + - name: Build data-provider + if: steps.cache-data-provider.outputs.cache-hit != 'true' run: npm run build:data-provider - - name: Install Data Schemas Package + - name: Restore data-schemas build cache + id: cache-data-schemas + uses: actions/cache@v4 + with: + path: packages/data-schemas/dist + key: build-data-schemas-${{ runner.os }}-${{ hashFiles('packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/rollup.config.js', 'packages/data-schemas/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }} + + - name: Build data-schemas + if: steps.cache-data-schemas.outputs.cache-hit != 'true' run: npm run build:data-schemas - - name: Install API Package + - name: Restore api build cache + id: cache-api + uses: actions/cache@v4 + with: + path: packages/api/dist + key: build-api-${{ runner.os }}-${{ hashFiles('packages/api/src/**', 'packages/api/tsconfig*.json', 'packages/api/server-rollup.config.js', 'packages/api/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json', 'packages/data-schemas/src/**', 'packages/data-schemas/tsconfig*.json', 'packages/data-schemas/rollup.config.js', 'packages/data-schemas/package.json') }} + + - name: Build api + if: steps.cache-api.outputs.cache-hit != 'true' run: npm run build:api - - name: Create empty auth.json file - run: | - mkdir -p api/data - echo '{}' > api/data/auth.json + - name: Upload data-provider build + uses: actions/upload-artifact@v4 + with: + name: build-data-provider + path: packages/data-provider/dist + retention-days: 2 - - name: Check for Circular dependency in rollup + - name: Upload data-schemas build + uses: actions/upload-artifact@v4 + with: + name: build-data-schemas + path: packages/data-schemas/dist + retention-days: 2 + + - name: Upload api build + uses: actions/upload-artifact@v4 + with: + name: build-api + path: packages/api/dist + retention-days: 2 + + circular-deps: + name: Circular dependency checks + needs: build + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 20.19 + uses: actions/setup-node@v4 + with: + node-version: '20.19' + + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + api/node_modules + packages/api/node_modules + packages/data-provider/node_modules + packages/data-schemas/node_modules + key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }} + + - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: npm ci + + - name: Download data-provider build + uses: actions/download-artifact@v4 + with: + name: build-data-provider + path: packages/data-provider/dist + + - name: Download data-schemas build + uses: actions/download-artifact@v4 + with: + name: build-data-schemas + path: packages/data-schemas/dist + + - name: Rebuild @librechat/api and check for circular dependencies + run: | + output=$(npm run build:api 2>&1) + echo "$output" + if echo "$output" | grep -q "Circular depend"; then + echo "Error: Circular dependency detected in @librechat/api!" + exit 1 + fi + + - name: Detect circular dependencies in rollup working-directory: ./packages/data-provider run: | output=$(npm run rollup:api) @@ -60,17 +157,201 @@ jobs: exit 1 fi + test-api: + name: 'Tests: api' + needs: build + runs-on: ubuntu-latest + timeout-minutes: 15 + env: + MONGO_URI: ${{ secrets.MONGO_URI }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + JWT_SECRET: ${{ secrets.JWT_SECRET }} + CREDS_KEY: ${{ secrets.CREDS_KEY }} + CREDS_IV: ${{ secrets.CREDS_IV }} + BAN_VIOLATIONS: ${{ secrets.BAN_VIOLATIONS }} + BAN_DURATION: ${{ secrets.BAN_DURATION }} + BAN_INTERVAL: ${{ secrets.BAN_INTERVAL }} + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 20.19 + uses: actions/setup-node@v4 + with: + node-version: '20.19' + + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + api/node_modules + packages/api/node_modules + packages/data-provider/node_modules + packages/data-schemas/node_modules + key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }} + + - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: npm ci + + - name: Download data-provider build + uses: actions/download-artifact@v4 + with: + name: build-data-provider + path: packages/data-provider/dist + + - name: Download data-schemas build + uses: actions/download-artifact@v4 + with: + name: build-data-schemas + path: packages/data-schemas/dist + + - name: Download api build + uses: actions/download-artifact@v4 + with: + name: build-api + path: packages/api/dist + + - name: Create empty auth.json file + run: | + mkdir -p api/data + echo '{}' > api/data/auth.json + - name: Prepare .env.test file run: cp api/test/.env.test.example api/test/.env.test - name: Run unit tests run: cd api && npm run test:ci - - name: Run librechat-data-provider unit tests + test-data-provider: + name: 'Tests: data-provider' + needs: build + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 20.19 + uses: actions/setup-node@v4 + with: + node-version: '20.19' + + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + api/node_modules + packages/api/node_modules + packages/data-provider/node_modules + packages/data-schemas/node_modules + key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }} + + - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: npm ci + + - name: Download data-provider build + uses: actions/download-artifact@v4 + with: + name: build-data-provider + path: packages/data-provider/dist + + - name: Run unit tests run: cd packages/data-provider && npm run test:ci - - name: Run @librechat/data-schemas unit tests + test-data-schemas: + name: 'Tests: data-schemas' + needs: build + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 20.19 + uses: actions/setup-node@v4 + with: + node-version: '20.19' + + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + api/node_modules + packages/api/node_modules + packages/data-provider/node_modules + packages/data-schemas/node_modules + key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }} + + - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: npm ci + + - name: Download data-provider build + uses: actions/download-artifact@v4 + with: + name: build-data-provider + path: packages/data-provider/dist + + - name: Download data-schemas build + uses: actions/download-artifact@v4 + with: + name: build-data-schemas + path: packages/data-schemas/dist + + - name: Run unit tests run: cd packages/data-schemas && npm run test:ci - - name: Run @librechat/api unit tests + test-packages-api: + name: 'Tests: @librechat/api' + needs: build + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 20.19 + uses: actions/setup-node@v4 + with: + node-version: '20.19' + + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + api/node_modules + packages/api/node_modules + packages/data-provider/node_modules + packages/data-schemas/node_modules + key: node-modules-backend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }} + + - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: npm ci + + - name: Download data-provider build + uses: actions/download-artifact@v4 + with: + name: build-data-provider + path: packages/data-provider/dist + + - name: Download data-schemas build + uses: actions/download-artifact@v4 + with: + name: build-data-schemas + path: packages/data-schemas/dist + + - name: Download api build + uses: actions/download-artifact@v4 + with: + name: build-api + path: packages/api/dist + + - name: Run unit tests run: cd packages/api && npm run test:ci diff --git a/.github/workflows/frontend-review.yml b/.github/workflows/frontend-review.yml index 989e2e4abe..9c2d4a37b1 100644 --- a/.github/workflows/frontend-review.yml +++ b/.github/workflows/frontend-review.yml @@ -2,7 +2,7 @@ name: Frontend Unit Tests on: pull_request: - branches: + branches: - main - dev - dev-staging @@ -11,51 +11,200 @@ on: - 'client/**' - 'packages/data-provider/**' +env: + NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}' + jobs: - tests_frontend_ubuntu: - name: Run frontend unit tests on Ubuntu - timeout-minutes: 60 + build: + name: Build packages runs-on: ubuntu-latest - env: - NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}' + timeout-minutes: 15 steps: - uses: actions/checkout@v4 - - name: Use Node.js 20.x + + - name: Use Node.js 20.19 uses: actions/setup-node@v4 with: - node-version: 20 - cache: 'npm' + node-version: '20.19' + + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + client/node_modules + packages/client/node_modules + packages/data-provider/node_modules + key: node-modules-frontend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }} - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' run: npm ci - - name: Build Client - run: npm run frontend:ci + - name: Restore data-provider build cache + id: cache-data-provider + uses: actions/cache@v4 + with: + path: packages/data-provider/dist + key: build-data-provider-${{ runner.os }}-${{ hashFiles('packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }} + + - name: Build data-provider + if: steps.cache-data-provider.outputs.cache-hit != 'true' + run: npm run build:data-provider + + - name: Restore client-package build cache + id: cache-client-package + uses: actions/cache@v4 + with: + path: packages/client/dist + key: build-client-package-${{ runner.os }}-${{ hashFiles('packages/client/src/**', 'packages/client/tsconfig*.json', 'packages/client/rollup.config.js', 'packages/client/package.json', 'packages/data-provider/src/**', 'packages/data-provider/tsconfig*.json', 'packages/data-provider/rollup.config.js', 'packages/data-provider/package.json') }} + + - name: Build client-package + if: steps.cache-client-package.outputs.cache-hit != 'true' + run: npm run build:client-package + + - name: Upload data-provider build + uses: actions/upload-artifact@v4 + with: + name: build-data-provider + path: packages/data-provider/dist + retention-days: 2 + + - name: Upload client-package build + uses: actions/upload-artifact@v4 + with: + name: build-client-package + path: packages/client/dist + retention-days: 2 + + test-ubuntu: + name: 'Tests: Ubuntu' + needs: build + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 20.19 + uses: actions/setup-node@v4 + with: + node-version: '20.19' + + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + client/node_modules + packages/client/node_modules + packages/data-provider/node_modules + key: node-modules-frontend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }} + + - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: npm ci + + - name: Download data-provider build + uses: actions/download-artifact@v4 + with: + name: build-data-provider + path: packages/data-provider/dist + + - name: Download client-package build + uses: actions/download-artifact@v4 + with: + name: build-client-package + path: packages/client/dist - name: Run unit tests run: npm run test:ci --verbose working-directory: client - tests_frontend_windows: - name: Run frontend unit tests on Windows - timeout-minutes: 60 + test-windows: + name: 'Tests: Windows' + needs: build runs-on: windows-latest - env: - NODE_OPTIONS: '--max-old-space-size=${{ secrets.NODE_MAX_OLD_SPACE_SIZE || 6144 }}' + timeout-minutes: 20 steps: - uses: actions/checkout@v4 - - name: Use Node.js 20.x + + - name: Use Node.js 20.19 uses: actions/setup-node@v4 with: - node-version: 20 - cache: 'npm' + node-version: '20.19' + + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + client/node_modules + packages/client/node_modules + packages/data-provider/node_modules + key: node-modules-frontend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }} - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' run: npm ci - - name: Build Client - run: npm run frontend:ci + - name: Download data-provider build + uses: actions/download-artifact@v4 + with: + name: build-data-provider + path: packages/data-provider/dist + + - name: Download client-package build + uses: actions/download-artifact@v4 + with: + name: build-client-package + path: packages/client/dist - name: Run unit tests run: npm run test:ci --verbose - working-directory: client \ No newline at end of file + working-directory: client + + build-verify: + name: Vite build verification + needs: build + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js 20.19 + uses: actions/setup-node@v4 + with: + node-version: '20.19' + + - name: Restore node_modules cache + id: cache-node-modules + uses: actions/cache@v4 + with: + path: | + node_modules + client/node_modules + packages/client/node_modules + packages/data-provider/node_modules + key: node-modules-frontend-${{ runner.os }}-20.19-${{ hashFiles('package-lock.json') }} + + - name: Install dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + run: npm ci + + - name: Download data-provider build + uses: actions/download-artifact@v4 + with: + name: build-data-provider + path: packages/data-provider/dist + + - name: Download client-package build + uses: actions/download-artifact@v4 + with: + name: build-client-package + path: packages/client/dist + + - name: Build client + run: cd client && npm run build:ci diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..ec44607aa7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,166 @@ +# LibreChat + +## Project Overview + +LibreChat is a monorepo with the following key workspaces: + +| Workspace | Language | Side | Dependency | Purpose | +|---|---|---|---|---| +| `/api` | JS (legacy) | Backend | `packages/api`, `packages/data-schemas`, `packages/data-provider`, `@librechat/agents` | Express server — minimize changes here | +| `/packages/api` | **TypeScript** | Backend | `packages/data-schemas`, `packages/data-provider` | New backend code lives here (TS only, consumed by `/api`) | +| `/packages/data-schemas` | TypeScript | Backend | `packages/data-provider` | Database models/schemas, shareable across backend projects | +| `/packages/data-provider` | TypeScript | Shared | — | Shared API types, endpoints, data-service — used by both frontend and backend | +| `/client` | TypeScript/React | Frontend | `packages/data-provider`, `packages/client` | Frontend SPA | +| `/packages/client` | TypeScript | Frontend | `packages/data-provider` | Shared frontend utilities | + +The source code for `@librechat/agents` (major backend dependency, same team) is at `/home/danny/agentus`. + +--- + +## Workspace Boundaries + +- **All new backend code must be TypeScript** in `/packages/api`. +- Keep `/api` changes to the absolute minimum (thin JS wrappers calling into `/packages/api`). +- Database-specific shared logic goes in `/packages/data-schemas`. +- Frontend/backend shared API logic (endpoints, types, data-service) goes in `/packages/data-provider`. +- Build data-provider from project root: `npm run build:data-provider`. + +--- + +## Code Style + +### Structure and Clarity + +- **Never-nesting**: early returns, flat code, minimal indentation. Break complex operations into well-named helpers. +- **Functional first**: pure functions, immutable data, `map`/`filter`/`reduce` over imperative loops. Only reach for OOP when it clearly improves domain modeling or state encapsulation. +- **No dynamic imports** unless absolutely necessary. + +### DRY + +- Extract repeated logic into utility functions. +- Reusable hooks / higher-order components for UI patterns. +- Parameterized helpers instead of near-duplicate functions. +- Constants for repeated values; configuration objects over duplicated init code. +- Shared validators, centralized error handling, single source of truth for business rules. +- Shared typing system with interfaces/types extending common base definitions. +- Abstraction layers for external API interactions. + +### Iteration and Performance + +- **Minimize looping** — especially over shared data structures like message arrays, which are iterated frequently throughout the codebase. Every additional pass adds up at scale. +- Consolidate sequential O(n) operations into a single pass whenever possible; never loop over the same collection twice if the work can be combined. +- Choose data structures that reduce the need to iterate (e.g., `Map`/`Set` for lookups instead of `Array.find`/`Array.includes`). +- Avoid unnecessary object creation; consider space-time tradeoffs. +- Prevent memory leaks: careful with closures, dispose resources/event listeners, no circular references. + +### Type Safety + +- **Never use `any`**. Explicit types for all parameters, return values, and variables. +- **Limit `unknown`** — avoid `unknown`, `Record`, and `as unknown as T` assertions. A `Record` almost always signals a missing explicit type definition. +- **Don't duplicate types** — before defining a new type, check whether it already exists in the project (especially `packages/data-provider`). Reuse and extend existing types rather than creating redundant definitions. +- Use union types, generics, and interfaces appropriately. +- All TypeScript and ESLint warnings/errors must be addressed — do not leave unresolved diagnostics. + +### Comments and Documentation + +- Write self-documenting code; no inline comments narrating what code does. +- JSDoc only for complex/non-obvious logic or intellisense on public APIs. +- Single-line JSDoc for brief docs, multi-line for complex cases. +- Avoid standalone `//` comments unless absolutely necessary. + +### Import Order + +Imports are organized into three sections: + +1. **Package imports** — sorted shortest to longest line length (`react` always first). +2. **`import type` imports** — sorted longest to shortest (package types first, then local types; length resets between sub-groups). +3. **Local/project imports** — sorted longest to shortest. + +Multi-line imports count total character length across all lines. Consolidate value imports from the same module. Always use standalone `import type { ... }` — never inline `type` inside value imports. + +### JS/TS Loop Preferences + +- **Limit looping as much as possible.** Prefer single-pass transformations and avoid re-iterating the same data. +- `for (let i = 0; ...)` for performance-critical or index-dependent operations. +- `for...of` for simple array iteration. +- `for...in` only for object property enumeration. + +--- + +## Frontend Rules (`client/src/**/*`) + +### Localization + +- All user-facing text must use `useLocalize()`. +- Only update English keys in `client/src/locales/en/translation.json` (other languages are automated externally). +- Semantic key prefixes: `com_ui_`, `com_assistants_`, etc. + +### Components + +- TypeScript for all React components with proper type imports. +- Semantic HTML with ARIA labels (`role`, `aria-label`) for accessibility. +- Group related components in feature directories (e.g., `SidePanel/Memories/`). +- Use index files for clean exports. + +### Data Management + +- Feature hooks: `client/src/data-provider/[Feature]/queries.ts` → `[Feature]/index.ts` → `client/src/data-provider/index.ts`. +- React Query (`@tanstack/react-query`) for all API interactions; proper query invalidation on mutations. +- QueryKeys and MutationKeys in `packages/data-provider/src/keys.ts`. + +### Data-Provider Integration + +- Endpoints: `packages/data-provider/src/api-endpoints.ts` +- Data service: `packages/data-provider/src/data-service.ts` +- Types: `packages/data-provider/src/types/queries.ts` +- Use `encodeURIComponent` for dynamic URL parameters. + +### Performance + +- Prioritize memory and speed efficiency at scale. +- Cursor pagination for large datasets. +- Proper dependency arrays to avoid unnecessary re-renders. +- Leverage React Query caching and background refetching. + +--- + +## Development Commands + +| Command | Purpose | +|---|---| +| `npm run smart-reinstall` | Install deps (if lockfile changed) + build via Turborepo | +| `npm run reinstall` | Clean install — wipe `node_modules` and reinstall from scratch | +| `npm run backend` | Start the backend server | +| `npm run backend:dev` | Start backend with file watching (development) | +| `npm run build` | Build all compiled code via Turborepo (parallel, cached) | +| `npm run frontend` | Build all compiled code sequentially (legacy fallback) | +| `npm run frontend:dev` | Start frontend dev server with HMR (port 3090, requires backend running) | +| `npm run build:data-provider` | Rebuild `packages/data-provider` after changes | + +- Node.js: v20.19.0+ or ^22.12.0 or >= 23.0.0 +- Database: MongoDB +- Backend runs on `http://localhost:3080/`; frontend dev server on `http://localhost:3090/` + +--- + +## Testing + +- Framework: **Jest**, run per-workspace. +- Run tests from their workspace directory: `cd api && npx jest `, `cd packages/api && npx jest `, etc. +- Frontend tests: `__tests__` directories alongside components; use `test/layout-test-utils` for rendering. +- Cover loading, success, and error states for UI/data flows. + +### Philosophy + +- **Real logic over mocks.** Exercise actual code paths with real dependencies. Mocking is a last resort. +- **Spies over mocks.** Assert that real functions are called with expected arguments and frequency without replacing underlying logic. +- **MongoDB**: use `mongodb-memory-server` for a real in-memory MongoDB instance. Test actual queries and schema validation, not mocked DB calls. +- **MCP**: use real `@modelcontextprotocol/sdk` exports for servers, transports, and tool definitions. Mirror real scenarios, don't stub SDK internals. +- Only mock what you cannot control: external HTTP APIs, rate-limited services, non-deterministic system calls. +- Heavy mocking is a code smell, not a testing strategy. + +--- + +## Formatting + +Fix all formatting lint errors (trailing spaces, tabs, newlines, indentation) using auto-fix when available. All TypeScript/ESLint warnings and errors **must** be resolved. diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index a8cb8282bd..0000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,236 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - - - - - - -## [Unreleased] - -### ✨ New Features - -- ✨ feat: implement search parameter updates by **@mawburn** in [#7151](https://github.com/danny-avila/LibreChat/pull/7151) -- 🎏 feat: Add MCP support for Streamable HTTP Transport by **@benverhees** in [#7353](https://github.com/danny-avila/LibreChat/pull/7353) -- 🔒 feat: Add Content Security Policy using Helmet middleware by **@rubentalstra** in [#7377](https://github.com/danny-avila/LibreChat/pull/7377) -- ✨ feat: Add Normalization for MCP Server Names by **@danny-avila** in [#7421](https://github.com/danny-avila/LibreChat/pull/7421) -- 📊 feat: Improve Helm Chart by **@hofq** in [#3638](https://github.com/danny-avila/LibreChat/pull/3638) -- 🦾 feat: Claude-4 Support by **@danny-avila** in [#7509](https://github.com/danny-avila/LibreChat/pull/7509) -- 🪨 feat: Bedrock Support for Claude-4 Reasoning by **@danny-avila** in [#7517](https://github.com/danny-avila/LibreChat/pull/7517) - -### 🌍 Internationalization - -- 🌍 i18n: Add `Danish` and `Czech` and `Catalan` localization support by **@rubentalstra** in [#7373](https://github.com/danny-avila/LibreChat/pull/7373) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7375](https://github.com/danny-avila/LibreChat/pull/7375) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7468](https://github.com/danny-avila/LibreChat/pull/7468) - -### 🔧 Fixes - -- 💬 fix: update aria-label for accessibility in ConvoLink component by **@berry-13** in [#7320](https://github.com/danny-avila/LibreChat/pull/7320) -- 🔑 fix: use `apiKey` instead of `openAIApiKey` in OpenAI-like Config by **@danny-avila** in [#7337](https://github.com/danny-avila/LibreChat/pull/7337) -- 🔄 fix: update navigation logic in `useFocusChatEffect` to ensure correct search parameters are used by **@mawburn** in [#7340](https://github.com/danny-avila/LibreChat/pull/7340) -- 🔄 fix: Improve MCP Connection Cleanup by **@danny-avila** in [#7400](https://github.com/danny-avila/LibreChat/pull/7400) -- 🛡️ fix: Preset and Validation Logic for URL Query Params by **@danny-avila** in [#7407](https://github.com/danny-avila/LibreChat/pull/7407) -- 🌘 fix: artifact of preview text is illegible in dark mode by **@nhtruong** in [#7405](https://github.com/danny-avila/LibreChat/pull/7405) -- 🛡️ fix: Temporarily Remove CSP until Configurable by **@danny-avila** in [#7419](https://github.com/danny-avila/LibreChat/pull/7419) -- 💽 fix: Exclude index page `/` from static cache settings by **@sbruel** in [#7382](https://github.com/danny-avila/LibreChat/pull/7382) - -### ⚙️ Other Changes - -- 📜 docs: CHANGELOG for release v0.7.8 by **@github-actions[bot]** in [#7290](https://github.com/danny-avila/LibreChat/pull/7290) -- 📦 chore: Update API Package Dependencies by **@danny-avila** in [#7359](https://github.com/danny-avila/LibreChat/pull/7359) -- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7321](https://github.com/danny-avila/LibreChat/pull/7321) -- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7434](https://github.com/danny-avila/LibreChat/pull/7434) -- 🛡️ chore: `multer` v2.0.0 for CVE-2025-47935 and CVE-2025-47944 by **@danny-avila** in [#7454](https://github.com/danny-avila/LibreChat/pull/7454) -- 📂 refactor: Improve `FileAttachment` & File Form Deletion by **@danny-avila** in [#7471](https://github.com/danny-avila/LibreChat/pull/7471) -- 📊 chore: Remove Old Helm Chart by **@hofq** in [#7512](https://github.com/danny-avila/LibreChat/pull/7512) -- 🪖 chore: bump helm app version to v0.7.8 by **@austin-barrington** in [#7524](https://github.com/danny-avila/LibreChat/pull/7524) - - - ---- -## [v0.7.8] - - -Changes from v0.7.8-rc1 to v0.7.8. - -### ✨ New Features - -- ✨ feat: Enhance form submission for touch screens by **@berry-13** in [#7198](https://github.com/danny-avila/LibreChat/pull/7198) -- 🔍 feat: Additional Tavily API Tool Parameters by **@glowforge-opensource** in [#7232](https://github.com/danny-avila/LibreChat/pull/7232) -- 🐋 feat: Add python to Dockerfile for increased MCP compatibility by **@technicalpickles** in [#7270](https://github.com/danny-avila/LibreChat/pull/7270) - -### 🔧 Fixes - -- 🔧 fix: Google Gemma Support & OpenAI Reasoning Instructions by **@danny-avila** in [#7196](https://github.com/danny-avila/LibreChat/pull/7196) -- 🛠️ fix: Conversation Navigation State by **@danny-avila** in [#7210](https://github.com/danny-avila/LibreChat/pull/7210) -- 🔄 fix: o-Series Model Regex for System Messages by **@danny-avila** in [#7245](https://github.com/danny-avila/LibreChat/pull/7245) -- 🔖 fix: Custom Headers for Initial MCP SSE Connection by **@danny-avila** in [#7246](https://github.com/danny-avila/LibreChat/pull/7246) -- 🛡️ fix: Deep Clone `MCPOptions` for User MCP Connections by **@danny-avila** in [#7247](https://github.com/danny-avila/LibreChat/pull/7247) -- 🔄 fix: URL Param Race Condition and File Draft Persistence by **@danny-avila** in [#7257](https://github.com/danny-avila/LibreChat/pull/7257) -- 🔄 fix: Assistants Endpoint & Minor Issues by **@danny-avila** in [#7274](https://github.com/danny-avila/LibreChat/pull/7274) -- 🔄 fix: Ollama Think Tag Edge Case with Tools by **@danny-avila** in [#7275](https://github.com/danny-avila/LibreChat/pull/7275) - -### ⚙️ Other Changes - -- 📜 docs: CHANGELOG for release v0.7.8-rc1 by **@github-actions[bot]** in [#7153](https://github.com/danny-avila/LibreChat/pull/7153) -- 🔄 refactor: Artifact Visibility Management by **@danny-avila** in [#7181](https://github.com/danny-avila/LibreChat/pull/7181) -- 📦 chore: Bump Package Security by **@danny-avila** in [#7183](https://github.com/danny-avila/LibreChat/pull/7183) -- 🌿 refactor: Unmount Fork Popover on Hide for Better Performance by **@danny-avila** in [#7189](https://github.com/danny-avila/LibreChat/pull/7189) -- 🧰 chore: ESLint configuration to enforce Prettier formatting rules by **@mawburn** in [#7186](https://github.com/danny-avila/LibreChat/pull/7186) -- 🎨 style: Improve KaTeX Rendering for LaTeX Equations by **@andresgit** in [#7223](https://github.com/danny-avila/LibreChat/pull/7223) -- 📝 docs: Update `.env.example` Google models by **@marlonka** in [#7254](https://github.com/danny-avila/LibreChat/pull/7254) -- 💬 refactor: MCP Chat Visibility Option, Google Rates, Remove OpenAPI Plugins by **@danny-avila** in [#7286](https://github.com/danny-avila/LibreChat/pull/7286) -- 📜 docs: Unreleased Changelog by **@github-actions[bot]** in [#7214](https://github.com/danny-avila/LibreChat/pull/7214) - - - -[See full release details][release-v0.7.8] - -[release-v0.7.8]: https://github.com/danny-avila/LibreChat/releases/tag/v0.7.8 - ---- -## [v0.7.8-rc1] - - -Changes from v0.7.7 to v0.7.8-rc1. - -### ✨ New Features - -- 🔍 feat: Mistral OCR API / Upload Files as Text by **@danny-avila** in [#6274](https://github.com/danny-avila/LibreChat/pull/6274) -- 🤖 feat: Support OpenAI Web Search models by **@danny-avila** in [#6313](https://github.com/danny-avila/LibreChat/pull/6313) -- 🔗 feat: Agent Chain (Mixture-of-Agents) by **@danny-avila** in [#6374](https://github.com/danny-avila/LibreChat/pull/6374) -- ⌛ feat: `initTimeout` for Slow Starting MCP Servers by **@perweij** in [#6383](https://github.com/danny-avila/LibreChat/pull/6383) -- 🚀 feat: `S3` Integration for File handling and Image uploads by **@rubentalstra** in [#6142](https://github.com/danny-avila/LibreChat/pull/6142) -- 🔒feat: Enable OpenID Auto-Redirect by **@leondape** in [#6066](https://github.com/danny-avila/LibreChat/pull/6066) -- 🚀 feat: Integrate `Azure Blob Storage` for file handling and image uploads by **@rubentalstra** in [#6153](https://github.com/danny-avila/LibreChat/pull/6153) -- 🚀 feat: Add support for custom `AWS` endpoint in `S3` by **@rubentalstra** in [#6431](https://github.com/danny-avila/LibreChat/pull/6431) -- 🚀 feat: Add support for LDAP STARTTLS in LDAP authentication by **@rubentalstra** in [#6438](https://github.com/danny-avila/LibreChat/pull/6438) -- 🚀 feat: Refactor schema exports and update package version to 0.0.4 by **@rubentalstra** in [#6455](https://github.com/danny-avila/LibreChat/pull/6455) -- 🔼 feat: Add Auto Submit For URL Query Params by **@mjaverto** in [#6440](https://github.com/danny-avila/LibreChat/pull/6440) -- 🛠 feat: Enhance Redis Integration, Rate Limiters & Log Headers by **@danny-avila** in [#6462](https://github.com/danny-avila/LibreChat/pull/6462) -- 💵 feat: Add Automatic Balance Refill by **@rubentalstra** in [#6452](https://github.com/danny-avila/LibreChat/pull/6452) -- 🗣️ feat: add support for gpt-4o-transcribe models by **@berry-13** in [#6483](https://github.com/danny-avila/LibreChat/pull/6483) -- 🎨 feat: UI Refresh for Enhanced UX by **@berry-13** in [#6346](https://github.com/danny-avila/LibreChat/pull/6346) -- 🌍 feat: Add support for Hungarian language localization by **@rubentalstra** in [#6508](https://github.com/danny-avila/LibreChat/pull/6508) -- 🚀 feat: Add Gemini 2.5 Token/Context Values, Increase Max Possible Output to 64k by **@danny-avila** in [#6563](https://github.com/danny-avila/LibreChat/pull/6563) -- 🚀 feat: Enhance MCP Connections For Multi-User Support by **@danny-avila** in [#6610](https://github.com/danny-avila/LibreChat/pull/6610) -- 🚀 feat: Enhance S3 URL Expiry with Refresh; fix: S3 File Deletion by **@danny-avila** in [#6647](https://github.com/danny-avila/LibreChat/pull/6647) -- 🚀 feat: enhance UI components and refactor settings by **@berry-13** in [#6625](https://github.com/danny-avila/LibreChat/pull/6625) -- 💬 feat: move TemporaryChat to the Header by **@berry-13** in [#6646](https://github.com/danny-avila/LibreChat/pull/6646) -- 🚀 feat: Use Model Specs + Specific Endpoints, Limit Providers for Agents by **@danny-avila** in [#6650](https://github.com/danny-avila/LibreChat/pull/6650) -- 🪙 feat: Sync Balance Config on Login by **@danny-avila** in [#6671](https://github.com/danny-avila/LibreChat/pull/6671) -- 🔦 feat: MCP Support for Non-Agent Endpoints by **@danny-avila** in [#6775](https://github.com/danny-avila/LibreChat/pull/6775) -- 🗃️ feat: Code Interpreter File Persistence between Sessions by **@danny-avila** in [#6790](https://github.com/danny-avila/LibreChat/pull/6790) -- 🖥️ feat: Code Interpreter API for Non-Agent Endpoints by **@danny-avila** in [#6803](https://github.com/danny-avila/LibreChat/pull/6803) -- ⚡ feat: Self-hosted Artifacts Static Bundler URL by **@danny-avila** in [#6827](https://github.com/danny-avila/LibreChat/pull/6827) -- 🐳 feat: Add Jemalloc and UV to Docker Builds by **@danny-avila** in [#6836](https://github.com/danny-avila/LibreChat/pull/6836) -- 🤖 feat: GPT-4.1 by **@danny-avila** in [#6880](https://github.com/danny-avila/LibreChat/pull/6880) -- 👋 feat: remove Edge TTS by **@berry-13** in [#6885](https://github.com/danny-avila/LibreChat/pull/6885) -- feat: nav optimization by **@berry-13** in [#5785](https://github.com/danny-avila/LibreChat/pull/5785) -- 🗺️ feat: Add Parameter Location Mapping for OpenAPI actions by **@peeeteeer** in [#6858](https://github.com/danny-avila/LibreChat/pull/6858) -- 🤖 feat: Support `o4-mini` and `o3` Models by **@danny-avila** in [#6928](https://github.com/danny-avila/LibreChat/pull/6928) -- 🎨 feat: OpenAI Image Tools (GPT-Image-1) by **@danny-avila** in [#7079](https://github.com/danny-avila/LibreChat/pull/7079) -- 🗓️ feat: Add Special Variables for Prompts & Agents, Prompt UI Improvements by **@danny-avila** in [#7123](https://github.com/danny-avila/LibreChat/pull/7123) - -### 🌍 Internationalization - -- 🌍 i18n: Add Thai Language Support and Update Translations by **@rubentalstra** in [#6219](https://github.com/danny-avila/LibreChat/pull/6219) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6220](https://github.com/danny-avila/LibreChat/pull/6220) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6240](https://github.com/danny-avila/LibreChat/pull/6240) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6241](https://github.com/danny-avila/LibreChat/pull/6241) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6277](https://github.com/danny-avila/LibreChat/pull/6277) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6414](https://github.com/danny-avila/LibreChat/pull/6414) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6505](https://github.com/danny-avila/LibreChat/pull/6505) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6530](https://github.com/danny-avila/LibreChat/pull/6530) -- 🌍 i18n: Add Persian Localization Support by **@rubentalstra** in [#6669](https://github.com/danny-avila/LibreChat/pull/6669) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#6667](https://github.com/danny-avila/LibreChat/pull/6667) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7126](https://github.com/danny-avila/LibreChat/pull/7126) -- 🌍 i18n: Update translation.json with latest translations by **@github-actions[bot]** in [#7148](https://github.com/danny-avila/LibreChat/pull/7148) - -### 👐 Accessibility - -- 🎨 a11y: Update Model Spec Description Text by **@berry-13** in [#6294](https://github.com/danny-avila/LibreChat/pull/6294) -- 🗑️ a11y: Add Accessible Name to Button for File Attachment Removal by **@kangabell** in [#6709](https://github.com/danny-avila/LibreChat/pull/6709) -- ⌨️ a11y: enhance accessibility & visual consistency by **@berry-13** in [#6866](https://github.com/danny-avila/LibreChat/pull/6866) -- 🙌 a11y: Searchbar/Conversations List Focus by **@danny-avila** in [#7096](https://github.com/danny-avila/LibreChat/pull/7096) -- 👐 a11y: Improve Fork and SplitText Accessibility by **@danny-avila** in [#7147](https://github.com/danny-avila/LibreChat/pull/7147) - -### 🔧 Fixes - -- 🐛 fix: Avatar Type Definitions in Agent/Assistant Schemas by **@danny-avila** in [#6235](https://github.com/danny-avila/LibreChat/pull/6235) -- 🔧 fix: MeiliSearch Field Error and Patch Incorrect Import by #6210 by **@rubentalstra** in [#6245](https://github.com/danny-avila/LibreChat/pull/6245) -- 🔏 fix: Enhance Two-Factor Authentication by **@rubentalstra** in [#6247](https://github.com/danny-avila/LibreChat/pull/6247) -- 🐛 fix: Await saveMessage in abortMiddleware to ensure proper execution by **@sh4shii** in [#6248](https://github.com/danny-avila/LibreChat/pull/6248) -- 🔧 fix: Axios Proxy Usage And Bump `mongoose` by **@danny-avila** in [#6298](https://github.com/danny-avila/LibreChat/pull/6298) -- 🔧 fix: comment out MCP servers to resolve service run issues by **@KunalScriptz** in [#6316](https://github.com/danny-avila/LibreChat/pull/6316) -- 🔧 fix: Update Token Calculations and Mapping, MCP `env` Initialization by **@danny-avila** in [#6406](https://github.com/danny-avila/LibreChat/pull/6406) -- 🐞 fix: Agent "Resend" Message Attachments + Source Icon Styling by **@danny-avila** in [#6408](https://github.com/danny-avila/LibreChat/pull/6408) -- 🐛 fix: Prevent Crash on Duplicate Message ID by **@Odrec** in [#6392](https://github.com/danny-avila/LibreChat/pull/6392) -- 🔐 fix: Invalid Key Length in 2FA Encryption by **@rubentalstra** in [#6432](https://github.com/danny-avila/LibreChat/pull/6432) -- 🏗️ fix: Fix Agents Token Spend Race Conditions, Expand Test Coverage by **@danny-avila** in [#6480](https://github.com/danny-avila/LibreChat/pull/6480) -- 🔃 fix: Draft Clearing, Claude Titles, Remove Default Vision Max Tokens by **@danny-avila** in [#6501](https://github.com/danny-avila/LibreChat/pull/6501) -- 🔧 fix: Update username reference to use user.name in greeting display by **@rubentalstra** in [#6534](https://github.com/danny-avila/LibreChat/pull/6534) -- 🔧 fix: S3 Download Stream with Key Extraction and Blob Storage Encoding for Vision by **@danny-avila** in [#6557](https://github.com/danny-avila/LibreChat/pull/6557) -- 🔧 fix: Mistral type strictness for `usage` & update token values/windows by **@danny-avila** in [#6562](https://github.com/danny-avila/LibreChat/pull/6562) -- 🔧 fix: Consolidate Text Parsing and TTS Edge Initialization by **@danny-avila** in [#6582](https://github.com/danny-avila/LibreChat/pull/6582) -- 🔧 fix: Ensure continuation in image processing on base64 encoding from Blob Storage by **@danny-avila** in [#6619](https://github.com/danny-avila/LibreChat/pull/6619) -- ✉️ fix: Fallback For User Name In Email Templates by **@danny-avila** in [#6620](https://github.com/danny-avila/LibreChat/pull/6620) -- 🔧 fix: Azure Blob Integration and File Source References by **@rubentalstra** in [#6575](https://github.com/danny-avila/LibreChat/pull/6575) -- 🐛 fix: Safeguard against undefined addedEndpoints by **@wipash** in [#6654](https://github.com/danny-avila/LibreChat/pull/6654) -- 🤖 fix: Gemini 2.5 Vision Support by **@danny-avila** in [#6663](https://github.com/danny-avila/LibreChat/pull/6663) -- 🔄 fix: Avatar & Error Handling Enhancements by **@danny-avila** in [#6687](https://github.com/danny-avila/LibreChat/pull/6687) -- 🔧 fix: Chat Middleware, Zod Conversion, Auto-Save and S3 URL Refresh by **@danny-avila** in [#6720](https://github.com/danny-avila/LibreChat/pull/6720) -- 🔧 fix: Agent Capability Checks & DocumentDB Compatibility for Agent Resource Removal by **@danny-avila** in [#6726](https://github.com/danny-avila/LibreChat/pull/6726) -- 🔄 fix: Improve audio MIME type detection and handling by **@berry-13** in [#6707](https://github.com/danny-avila/LibreChat/pull/6707) -- 🪺 fix: Update Role Handling due to New Schema Shape by **@danny-avila** in [#6774](https://github.com/danny-avila/LibreChat/pull/6774) -- 🗨️ fix: Show ModelSpec Greeting by **@berry-13** in [#6770](https://github.com/danny-avila/LibreChat/pull/6770) -- 🔧 fix: Keyv and Proxy Issues, and More Memory Optimizations by **@danny-avila** in [#6867](https://github.com/danny-avila/LibreChat/pull/6867) -- ✨ fix: Implement dynamic text sizing for greeting and name display by **@berry-13** in [#6833](https://github.com/danny-avila/LibreChat/pull/6833) -- 📝 fix: Mistral OCR Image Support and Azure Agent Titles by **@danny-avila** in [#6901](https://github.com/danny-avila/LibreChat/pull/6901) -- 📢 fix: Invalid `engineTTS` and Conversation State on Navigation by **@berry-13** in [#6904](https://github.com/danny-avila/LibreChat/pull/6904) -- 🛠️ fix: Improve Accessibility and Display of Conversation Menu by **@danny-avila** in [#6913](https://github.com/danny-avila/LibreChat/pull/6913) -- 🔧 fix: Agent Resource Form, Convo Menu Style, Ensure Draft Clears on Submission by **@danny-avila** in [#6925](https://github.com/danny-avila/LibreChat/pull/6925) -- 🔀 fix: MCP Improvements, Auto-Save Drafts, Artifact Markup by **@danny-avila** in [#7040](https://github.com/danny-avila/LibreChat/pull/7040) -- 🐋 fix: Improve Deepseek Compatbility by **@danny-avila** in [#7132](https://github.com/danny-avila/LibreChat/pull/7132) -- 🐙 fix: Add Redis Ping Interval to Prevent Connection Drops by **@peeeteeer** in [#7127](https://github.com/danny-avila/LibreChat/pull/7127) - -### ⚙️ Other Changes - -- 📦 refactor: Move DB Models to `@librechat/data-schemas` by **@rubentalstra** in [#6210](https://github.com/danny-avila/LibreChat/pull/6210) -- 📦 chore: Patch `axios` to address CVE-2025-27152 by **@danny-avila** in [#6222](https://github.com/danny-avila/LibreChat/pull/6222) -- ⚠️ refactor: Use Error Content Part Instead Of Throwing Error for Agents by **@danny-avila** in [#6262](https://github.com/danny-avila/LibreChat/pull/6262) -- 🏃‍♂️ refactor: Improve Agent Run Context & Misc. Changes by **@danny-avila** in [#6448](https://github.com/danny-avila/LibreChat/pull/6448) -- 📝 docs: librechat.example.yaml by **@ineiti** in [#6442](https://github.com/danny-avila/LibreChat/pull/6442) -- 🏃‍♂️ refactor: More Agent Context Improvements during Run by **@danny-avila** in [#6477](https://github.com/danny-avila/LibreChat/pull/6477) -- 🔃 refactor: Allow streaming for `o1` models by **@danny-avila** in [#6509](https://github.com/danny-avila/LibreChat/pull/6509) -- 🔧 chore: `Vite` Plugin Upgrades & Config Optimizations by **@rubentalstra** in [#6547](https://github.com/danny-avila/LibreChat/pull/6547) -- 🔧 refactor: Consolidate Logging, Model Selection & Actions Optimizations, Minor Fixes by **@danny-avila** in [#6553](https://github.com/danny-avila/LibreChat/pull/6553) -- 🎨 style: Address Minor UI Refresh Issues by **@berry-13** in [#6552](https://github.com/danny-avila/LibreChat/pull/6552) -- 🔧 refactor: Enhance Model & Endpoint Configurations with Global Indicators 🌍 by **@berry-13** in [#6578](https://github.com/danny-avila/LibreChat/pull/6578) -- 💬 style: Chat UI, Greeting, and Message adjustments by **@berry-13** in [#6612](https://github.com/danny-avila/LibreChat/pull/6612) -- ⚡ refactor: DocumentDB Compatibility for Balance Updates by **@danny-avila** in [#6673](https://github.com/danny-avila/LibreChat/pull/6673) -- 🧹 chore: Update ESLint rules for React hooks by **@rubentalstra** in [#6685](https://github.com/danny-avila/LibreChat/pull/6685) -- 🪙 chore: Update Gemini Pricing by **@RedwindA** in [#6731](https://github.com/danny-avila/LibreChat/pull/6731) -- 🪺 refactor: Nest Permission fields for Roles by **@rubentalstra** in [#6487](https://github.com/danny-avila/LibreChat/pull/6487) -- 📦 chore: Update `caniuse-lite` dependency to version 1.0.30001706 by **@rubentalstra** in [#6482](https://github.com/danny-avila/LibreChat/pull/6482) -- ⚙️ refactor: OAuth Flow Signal, Type Safety, Tool Progress & Updated Packages by **@danny-avila** in [#6752](https://github.com/danny-avila/LibreChat/pull/6752) -- 📦 chore: bump vite from 6.2.3 to 6.2.5 by **@dependabot[bot]** in [#6745](https://github.com/danny-avila/LibreChat/pull/6745) -- 💾 chore: Enhance Local Storage Handling and Update MCP SDK by **@danny-avila** in [#6809](https://github.com/danny-avila/LibreChat/pull/6809) -- 🤖 refactor: Improve Agents Memory Usage, Bump Keyv, Grok 3 by **@danny-avila** in [#6850](https://github.com/danny-avila/LibreChat/pull/6850) -- 💾 refactor: Enhance Memory In Image Encodings & Client Disposal by **@danny-avila** in [#6852](https://github.com/danny-avila/LibreChat/pull/6852) -- 🔁 refactor: Token Event Handler and Standardize `maxTokens` Key by **@danny-avila** in [#6886](https://github.com/danny-avila/LibreChat/pull/6886) -- 🔍 refactor: Search & Message Retrieval by **@berry-13** in [#6903](https://github.com/danny-avila/LibreChat/pull/6903) -- 🎨 style: standardize dropdown styling & fix z-Index layering by **@berry-13** in [#6939](https://github.com/danny-avila/LibreChat/pull/6939) -- 📙 docs: CONTRIBUTING.md by **@dblock** in [#6831](https://github.com/danny-avila/LibreChat/pull/6831) -- 🧭 refactor: Modernize Nav/Header by **@danny-avila** in [#7094](https://github.com/danny-avila/LibreChat/pull/7094) -- 🪶 refactor: Chat Input Focus for Conversation Navigations & ChatForm Optimizations by **@danny-avila** in [#7100](https://github.com/danny-avila/LibreChat/pull/7100) -- 🔃 refactor: Streamline Navigation, Message Loading UX by **@danny-avila** in [#7118](https://github.com/danny-avila/LibreChat/pull/7118) -- 📜 docs: Unreleased changelog by **@github-actions[bot]** in [#6265](https://github.com/danny-avila/LibreChat/pull/6265) - - - -[See full release details][release-v0.7.8-rc1] - -[release-v0.7.8-rc1]: https://github.com/danny-avila/LibreChat/releases/tag/v0.7.8-rc1 - ---- diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000000..47dc3e3d86 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 38273bc5eb..02bda8a589 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# v0.8.2 +# v0.8.4-rc1 # Base node image FROM node:20-alpine AS node diff --git a/Dockerfile.multi b/Dockerfile.multi index 47e00d0fa8..8e7483e378 100644 --- a/Dockerfile.multi +++ b/Dockerfile.multi @@ -1,5 +1,5 @@ # Dockerfile.multi -# v0.8.2 +# v0.8.4-rc1 # Set configurable max-old-space-size with default ARG NODE_MAX_OLD_SPACE_SIZE=6144 diff --git a/README.md b/README.md index 6e04396637..e82b3ebc2c 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@

- - Deploy on Railway + + Deploy on Railway Deploy on Zeabur diff --git a/api/app/clients/BaseClient.js b/api/app/clients/BaseClient.js index 7ec1b70711..4ea54f9739 100644 --- a/api/app/clients/BaseClient.js +++ b/api/app/clients/BaseClient.js @@ -3,7 +3,9 @@ const fetch = require('node-fetch'); const { logger } = require('@librechat/data-schemas'); const { countTokens, + checkBalance, getBalanceConfig, + buildMessageFiles, extractFileContext, encodeAndFormatAudios, encodeAndFormatVideos, @@ -20,19 +22,13 @@ const { isAgentsEndpoint, isEphemeralAgentId, supportsBalanceCheck, + isBedrockDocumentType, } = require('librechat-data-provider'); -const { - updateMessage, - getMessages, - saveMessage, - saveConvo, - getConvo, - getFiles, -} = require('~/models'); const { getStrategyFunctions } = require('~/server/services/Files/strategies'); -const { checkBalance } = require('~/models/balanceMethods'); const { truncateToolCallOutputs } = require('./prompts'); +const { logViolation } = require('~/cache'); const TextStream = require('./TextStream'); +const db = require('~/models'); class BaseClient { constructor(apiKey, options = {}) { @@ -122,7 +118,9 @@ class BaseClient { * @returns {number} */ getTokenCountForResponse(responseMessage) { - logger.debug('[BaseClient] `recordTokenUsage` not implemented.', responseMessage); + logger.debug('[BaseClient] `recordTokenUsage` not implemented.', { + messageId: responseMessage?.messageId, + }); } /** @@ -133,12 +131,14 @@ class BaseClient { * @param {AppConfig['balance']} [balance] * @param {number} promptTokens * @param {number} completionTokens + * @param {string} [messageId] * @returns {Promise} */ - async recordTokenUsage({ model, balance, promptTokens, completionTokens }) { + async recordTokenUsage({ model, balance, promptTokens, completionTokens, messageId }) { logger.debug('[BaseClient] `recordTokenUsage` not implemented.', { model, balance, + messageId, promptTokens, completionTokens, }); @@ -659,16 +659,27 @@ class BaseClient { ); if (tokenCountMap) { - logger.debug('[BaseClient] tokenCountMap', tokenCountMap); if (tokenCountMap[userMessage.messageId]) { userMessage.tokenCount = tokenCountMap[userMessage.messageId]; - logger.debug('[BaseClient] userMessage', userMessage); + logger.debug('[BaseClient] userMessage', { + messageId: userMessage.messageId, + tokenCount: userMessage.tokenCount, + conversationId: userMessage.conversationId, + }); } this.handleTokenCountMap(tokenCountMap); } if (!isEdited && !this.skipSaveUserMessage) { + const reqFiles = this.options.req?.body?.files; + if (reqFiles && Array.isArray(this.options.attachments)) { + const files = buildMessageFiles(reqFiles, this.options.attachments); + if (files.length > 0) { + userMessage.files = files; + } + delete userMessage.image_urls; + } userMessagePromise = this.saveMessageToDatabase(userMessage, saveOptions, user); this.savedMessageIds.add(userMessage.messageId); if (typeof opts?.getReqData === 'function') { @@ -683,18 +694,26 @@ class BaseClient { balanceConfig?.enabled && supportsBalanceCheck[this.options.endpointType ?? this.options.endpoint] ) { - await checkBalance({ - req: this.options.req, - res: this.options.res, - txData: { - user: this.user, - tokenType: 'prompt', - amount: promptTokens, - endpoint: this.options.endpoint, - model: this.modelOptions?.model ?? this.model, - endpointTokenConfig: this.options.endpointTokenConfig, + await checkBalance( + { + req: this.options.req, + res: this.options.res, + txData: { + user: this.user, + tokenType: 'prompt', + amount: promptTokens, + endpoint: this.options.endpoint, + model: this.modelOptions?.model ?? this.model, + endpointTokenConfig: this.options.endpointTokenConfig, + }, }, - }); + { + logViolation, + getMultiplier: db.getMultiplier, + findBalanceByUser: db.findBalanceByUser, + createAutoRefillTransaction: db.createAutoRefillTransaction, + }, + ); } const { completion, metadata } = await this.sendCompletion(payload, opts); @@ -780,9 +799,18 @@ class BaseClient { promptTokens, completionTokens, balance: balanceConfig, - model: responseMessage.model, + /** Note: When using agents, responseMessage.model is the agent ID, not the model */ + model: this.model, + messageId: this.responseMessageId, }); } + + logger.debug('[BaseClient] Response token usage', { + messageId: responseMessage.messageId, + model: responseMessage.model, + promptTokens, + completionTokens, + }); } if (userMessagePromise) { @@ -882,7 +910,7 @@ class BaseClient { async loadHistory(conversationId, parentMessageId = null) { logger.debug('[BaseClient] Loading history:', { conversationId, parentMessageId }); - const messages = (await getMessages({ conversationId })) ?? []; + const messages = (await db.getMessages({ conversationId })) ?? []; if (messages.length === 0) { return []; @@ -938,8 +966,13 @@ class BaseClient { } const hasAddedConvo = this.options?.req?.body?.addedConvo != null; - const savedMessage = await saveMessage( - this.options?.req, + const reqCtx = { + userId: this.options?.req?.user?.id, + isTemporary: this.options?.req?.body?.isTemporary, + interfaceConfig: this.options?.req?.config?.interfaceConfig, + }; + const savedMessage = await db.saveMessage( + reqCtx, { ...message, endpoint: this.options.endpoint, @@ -964,7 +997,7 @@ class BaseClient { const existingConvo = this.fetchedConvo === true ? null - : await getConvo(this.options?.req?.user?.id, message.conversationId); + : await db.getConvo(this.options?.req?.user?.id, message.conversationId); const unsetFields = {}; const exceptions = new Set(['spec', 'iconURL']); @@ -991,7 +1024,7 @@ class BaseClient { } } - const conversation = await saveConvo(this.options?.req, fieldsToKeep, { + const conversation = await db.saveConvo(reqCtx, fieldsToKeep, { context: 'api/app/clients/BaseClient.js - saveMessageToDatabase #saveConvo', unsetFields, }); @@ -1004,7 +1037,7 @@ class BaseClient { * @param {Partial} message */ async updateMessageInDatabase(message) { - await updateMessage(this.options.req, message); + await db.updateMessage(this.options?.req?.user?.id, message); } /** @@ -1299,6 +1332,9 @@ class BaseClient { const allFiles = []; + const provider = this.options.agent?.provider ?? this.options.endpoint; + const isBedrock = provider === EModelEndpoint.bedrock; + for (const file of attachments) { /** @type {FileSources} */ const source = file.source ?? FileSources.local; @@ -1316,6 +1352,9 @@ class BaseClient { } else if (file.type === 'application/pdf') { categorizedAttachments.documents.push(file); allFiles.push(file); + } else if (isBedrock && isBedrockDocumentType(file.type)) { + categorizedAttachments.documents.push(file); + allFiles.push(file); } else if (file.type.startsWith('video/')) { categorizedAttachments.videos.push(file); allFiles.push(file); @@ -1398,7 +1437,7 @@ class BaseClient { return message; } - const files = await getFiles( + const files = await db.getFiles( { file_id: { $in: fileIds }, }, diff --git a/api/app/clients/specs/BaseClient.test.js b/api/app/clients/specs/BaseClient.test.js index fed80de28c..f13c9979ac 100644 --- a/api/app/clients/specs/BaseClient.test.js +++ b/api/app/clients/specs/BaseClient.test.js @@ -821,6 +821,56 @@ describe('BaseClient', () => { }); }); + describe('recordTokenUsage model assignment', () => { + test('should pass this.model to recordTokenUsage, not the agent ID from responseMessage.model', async () => { + const actualModel = 'claude-opus-4-5'; + const agentId = 'agent_p5Z_IU6EIxBoqn1BoqLBp'; + + TestClient.model = actualModel; + TestClient.options.endpoint = 'agents'; + TestClient.options.agent = { id: agentId }; + + TestClient.getTokenCountForResponse = jest.fn().mockReturnValue(50); + TestClient.recordTokenUsage = jest.fn().mockResolvedValue(undefined); + TestClient.buildMessages.mockReturnValue({ + prompt: [], + tokenCountMap: { res: 50 }, + }); + + await TestClient.sendMessage('Hello', {}); + + expect(TestClient.recordTokenUsage).toHaveBeenCalledWith( + expect.objectContaining({ + model: actualModel, + }), + ); + + const callArgs = TestClient.recordTokenUsage.mock.calls[0][0]; + expect(callArgs.model).not.toBe(agentId); + }); + + test('should pass this.model even when this.model differs from modelOptions.model', async () => { + const instanceModel = 'gpt-4o'; + TestClient.model = instanceModel; + TestClient.modelOptions = { model: 'gpt-4o-mini' }; + + TestClient.getTokenCountForResponse = jest.fn().mockReturnValue(50); + TestClient.recordTokenUsage = jest.fn().mockResolvedValue(undefined); + TestClient.buildMessages.mockReturnValue({ + prompt: [], + tokenCountMap: { res: 50 }, + }); + + await TestClient.sendMessage('Hello', {}); + + expect(TestClient.recordTokenUsage).toHaveBeenCalledWith( + expect.objectContaining({ + model: instanceModel, + }), + ); + }); + }); + describe('getMessagesWithinTokenLimit with instructions', () => { test('should always include instructions when present', async () => { TestClient.maxContextTokens = 50; @@ -928,4 +978,123 @@ describe('BaseClient', () => { expect(result.remainingContextTokens).toBe(2); // 25 - 20 - 3(assistant label) }); }); + + describe('sendMessage file population', () => { + const attachment = { + file_id: 'file-abc', + filename: 'image.png', + filepath: '/uploads/image.png', + type: 'image/png', + bytes: 1024, + object: 'file', + user: 'user-1', + embedded: false, + usage: 0, + text: 'large ocr blob that should be stripped', + _id: 'mongo-id-1', + }; + + beforeEach(() => { + TestClient.options.req = { body: { files: [{ file_id: 'file-abc' }] } }; + TestClient.options.attachments = [attachment]; + }); + + test('populates userMessage.files before saveMessageToDatabase is called', async () => { + TestClient.saveMessageToDatabase = jest.fn().mockImplementation((msg) => { + return Promise.resolve({ message: msg }); + }); + + await TestClient.sendMessage('Hello'); + + const userSave = TestClient.saveMessageToDatabase.mock.calls.find( + ([msg]) => msg.isCreatedByUser, + ); + expect(userSave).toBeDefined(); + expect(userSave[0].files).toBeDefined(); + expect(userSave[0].files).toHaveLength(1); + expect(userSave[0].files[0].file_id).toBe('file-abc'); + }); + + test('strips text and _id from files before saving', async () => { + TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} }); + + await TestClient.sendMessage('Hello'); + + const userSave = TestClient.saveMessageToDatabase.mock.calls.find( + ([msg]) => msg.isCreatedByUser, + ); + expect(userSave[0].files[0].text).toBeUndefined(); + expect(userSave[0].files[0]._id).toBeUndefined(); + expect(userSave[0].files[0].filename).toBe('image.png'); + }); + + test('deletes image_urls from userMessage when files are present', async () => { + TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} }); + TestClient.options.attachments = [ + { ...attachment, image_urls: ['data:image/png;base64,...'] }, + ]; + + await TestClient.sendMessage('Hello'); + + const userSave = TestClient.saveMessageToDatabase.mock.calls.find( + ([msg]) => msg.isCreatedByUser, + ); + expect(userSave[0].image_urls).toBeUndefined(); + }); + + test('does not set files when no attachments match request file IDs', async () => { + TestClient.options.req = { body: { files: [{ file_id: 'file-nomatch' }] } }; + TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} }); + + await TestClient.sendMessage('Hello'); + + const userSave = TestClient.saveMessageToDatabase.mock.calls.find( + ([msg]) => msg.isCreatedByUser, + ); + expect(userSave[0].files).toBeUndefined(); + }); + + test('skips file population when attachments is not an array (Promise case)', async () => { + TestClient.options.attachments = Promise.resolve([attachment]); + TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} }); + + await TestClient.sendMessage('Hello'); + + const userSave = TestClient.saveMessageToDatabase.mock.calls.find( + ([msg]) => msg.isCreatedByUser, + ); + expect(userSave[0].files).toBeUndefined(); + }); + + test('skips file population when skipSaveUserMessage is true', async () => { + TestClient.skipSaveUserMessage = true; + TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} }); + + await TestClient.sendMessage('Hello'); + + const userSave = TestClient.saveMessageToDatabase.mock.calls.find( + ([msg]) => msg?.isCreatedByUser, + ); + expect(userSave).toBeUndefined(); + }); + + test('ignores file_id: undefined entries in req.body.files (no set poisoning)', async () => { + TestClient.options.req = { + body: { files: [{ file_id: undefined }, { file_id: 'file-abc' }] }, + }; + TestClient.options.attachments = [ + { ...attachment, file_id: undefined }, + { ...attachment, file_id: 'file-abc' }, + ]; + TestClient.saveMessageToDatabase = jest.fn().mockResolvedValue({ message: {} }); + + await TestClient.sendMessage('Hello'); + + const userSave = TestClient.saveMessageToDatabase.mock.calls.find( + ([msg]) => msg.isCreatedByUser, + ); + expect(userSave[0].files).toHaveLength(1); + expect(userSave[0].files[0].file_id).toBe('file-abc'); + }); + }); }); diff --git a/api/app/clients/tools/manifest.json b/api/app/clients/tools/manifest.json index 7930e67ac9..9637c20867 100644 --- a/api/app/clients/tools/manifest.json +++ b/api/app/clients/tools/manifest.json @@ -16,7 +16,7 @@ "name": "Google", "pluginKey": "google", "description": "Use Google Search to find information about the weather, news, sports, and more.", - "icon": "https://i.imgur.com/SMmVkNB.png", + "icon": "assets/google-search.svg", "authConfig": [ { "authField": "GOOGLE_CSE_ID", @@ -61,7 +61,7 @@ "name": "DALL-E-3", "pluginKey": "dalle", "description": "[DALL-E-3] Create realistic images and art from a description in natural language", - "icon": "https://i.imgur.com/u2TzXzH.png", + "icon": "assets/openai.svg", "authConfig": [ { "authField": "DALLE3_API_KEY||DALLE_API_KEY", @@ -74,7 +74,7 @@ "name": "Tavily Search", "pluginKey": "tavily_search_results_json", "description": "Tavily Search is a robust search API tailored for LLM Agents. It seamlessly integrates with diverse data sources to ensure a superior, relevant search experience.", - "icon": "https://tavily.com/favicon.ico", + "icon": "assets/tavily.svg", "authConfig": [ { "authField": "TAVILY_API_KEY", @@ -87,14 +87,14 @@ "name": "Calculator", "pluginKey": "calculator", "description": "Perform simple and complex mathematical calculations.", - "icon": "https://i.imgur.com/RHsSG5h.png", + "icon": "assets/calculator.svg", "authConfig": [] }, { "name": "Stable Diffusion", "pluginKey": "stable-diffusion", "description": "Generate photo-realistic images given any text input.", - "icon": "https://i.imgur.com/Yr466dp.png", + "icon": "assets/stability-ai.svg", "authConfig": [ { "authField": "SD_WEBUI_URL", @@ -107,7 +107,7 @@ "name": "Azure AI Search", "pluginKey": "azure-ai-search", "description": "Use Azure AI Search to find information", - "icon": "https://i.imgur.com/E7crPze.png", + "icon": "assets/azure-ai-search.svg", "authConfig": [ { "authField": "AZURE_AI_SEARCH_SERVICE_ENDPOINT", @@ -143,7 +143,7 @@ "name": "Flux", "pluginKey": "flux", "description": "Generate images using text with the Flux API.", - "icon": "https://blackforestlabs.ai/wp-content/uploads/2024/07/bfl_logo_retraced_blk.png", + "icon": "assets/bfl-ai.svg", "isAuthRequired": "true", "authConfig": [ { @@ -156,14 +156,14 @@ { "name": "Gemini Image Tools", "pluginKey": "gemini_image_gen", - "toolkit": true, "description": "Generate high-quality images using Google's Gemini Image Models. Supports Gemini API or Vertex AI.", "icon": "assets/gemini_image_gen.svg", "authConfig": [ { - "authField": "GEMINI_API_KEY||GOOGLE_KEY||GEMINI_VERTEX_ENABLED", - "label": "Gemini API Key (Optional if Vertex AI is configured)", - "description": "Your Google Gemini API Key from Google AI Studio. Leave blank if using Vertex AI with service account." + "authField": "GEMINI_API_KEY||GOOGLE_KEY||GOOGLE_SERVICE_KEY_FILE", + "label": "Gemini API Key (optional)", + "description": "Your Google Gemini API Key from Google AI Studio. Leave blank to use Vertex AI with a service account (GOOGLE_SERVICE_KEY_FILE or api/data/auth.json).", + "optional": true } ] } diff --git a/api/app/clients/tools/structured/GeminiImageGen.js b/api/app/clients/tools/structured/GeminiImageGen.js index c0e5a0ce1d..f197f1d41b 100644 --- a/api/app/clients/tools/structured/GeminiImageGen.js +++ b/api/app/clients/tools/structured/GeminiImageGen.js @@ -1,4 +1,3 @@ -const fs = require('fs'); const path = require('path'); const sharp = require('sharp'); const { v4 } = require('uuid'); @@ -6,12 +5,7 @@ const { ProxyAgent } = require('undici'); const { GoogleGenAI } = require('@google/genai'); const { tool } = require('@langchain/core/tools'); const { logger } = require('@librechat/data-schemas'); -const { - FileContext, - ContentTypes, - FileSources, - EImageOutputType, -} = require('librechat-data-provider'); +const { ContentTypes, EImageOutputType } = require('librechat-data-provider'); const { geminiToolkit, loadServiceKey, @@ -19,8 +13,7 @@ const { getTransactionsConfig, } = require('@librechat/api'); const { getStrategyFunctions } = require('~/server/services/Files/strategies'); -const { spendTokens } = require('~/models/spendTokens'); -const { getFiles } = require('~/models/File'); +const { spendTokens, getFiles } = require('~/models'); /** * Configure proxy support for Google APIs @@ -59,17 +52,12 @@ const displayMessage = * @returns {string} - The processed string */ function replaceUnwantedChars(inputString) { - return inputString?.replace(/[^\w\s\-_.,!?()]/g, '') || ''; -} - -/** - * Validate and sanitize image format - * @param {string} format - The format to validate - * @returns {string} - Safe format - */ -function getSafeFormat(format) { - const allowedFormats = ['png', 'jpg', 'jpeg', 'webp', 'gif']; - return allowedFormats.includes(format?.toLowerCase()) ? format.toLowerCase() : 'png'; + return ( + inputString + ?.replace(/\r\n|\r|\n/g, ' ') + .replace(/"/g, '') + .trim() || '' + ); } /** @@ -117,11 +105,8 @@ async function initializeGeminiClient(options = {}) { return new GoogleGenAI({ apiKey: googleKey }); } - // Fall back to Vertex AI with service account logger.debug('[GeminiImageGen] Using Vertex AI with service account'); const credentialsPath = getDefaultServiceKeyPath(); - - // Use loadServiceKey for consistent loading (supports file paths, JSON strings, base64) const serviceKey = await loadServiceKey(credentialsPath); if (!serviceKey || !serviceKey.project_id) { @@ -131,75 +116,14 @@ async function initializeGeminiClient(options = {}) { ); } - // Set GOOGLE_APPLICATION_CREDENTIALS for any Google Cloud SDK dependencies - try { - await fs.promises.access(credentialsPath); - process.env.GOOGLE_APPLICATION_CREDENTIALS = credentialsPath; - } catch { - // File doesn't exist, skip setting env var - } - return new GoogleGenAI({ vertexai: true, project: serviceKey.project_id, location: process.env.GOOGLE_LOC || process.env.GOOGLE_CLOUD_LOCATION || 'global', + googleAuthOptions: { credentials: serviceKey }, }); } -/** - * Save image to local filesystem - * @param {string} base64Data - Base64 encoded image data - * @param {string} format - Image format - * @param {string} userId - User ID - * @returns {Promise} - The relative URL - */ -async function saveImageLocally(base64Data, format, userId) { - const safeFormat = getSafeFormat(format); - const safeUserId = userId ? path.basename(userId) : 'default'; - const imageName = `gemini-img-${v4()}.${safeFormat}`; - const userDir = path.join(process.cwd(), 'client/public/images', safeUserId); - - await fs.promises.mkdir(userDir, { recursive: true }); - - const filePath = path.join(userDir, imageName); - await fs.promises.writeFile(filePath, Buffer.from(base64Data, 'base64')); - - logger.debug('[GeminiImageGen] Image saved locally to:', filePath); - return `/images/${safeUserId}/${imageName}`; -} - -/** - * Save image to cloud storage - * @param {Object} params - Parameters - * @returns {Promise} - The storage URL or null - */ -async function saveToCloudStorage({ base64Data, format, processFileURL, fileStrategy, userId }) { - if (!processFileURL || !fileStrategy || !userId) { - return null; - } - - try { - const safeFormat = getSafeFormat(format); - const safeUserId = path.basename(userId); - const dataURL = `data:image/${safeFormat};base64,${base64Data}`; - const imageName = `gemini-img-${v4()}.${safeFormat}`; - - const result = await processFileURL({ - URL: dataURL, - basePath: 'images', - userId: safeUserId, - fileName: imageName, - fileStrategy, - context: FileContext.image_generation, - }); - - return result.filepath; - } catch (error) { - logger.error('[GeminiImageGen] Error saving to cloud storage:', error); - return null; - } -} - /** * Convert image files to Gemini inline data format * @param {Object} params - Parameters @@ -326,8 +250,9 @@ function checkForSafetyBlock(response) { * @param {string} params.userId - The user ID * @param {string} params.conversationId - The conversation ID * @param {string} params.model - The model name + * @param {string} [params.messageId] - The response message ID for transaction correlation */ -async function recordTokenUsage({ usageMetadata, req, userId, conversationId, model }) { +async function recordTokenUsage({ usageMetadata, req, userId, conversationId, model, messageId }) { if (!usageMetadata) { logger.debug('[GeminiImageGen] No usage metadata available for balance tracking'); return; @@ -363,6 +288,7 @@ async function recordTokenUsage({ usageMetadata, req, userId, conversationId, mo { user: userId, model, + messageId, conversationId, context: 'image_generation', balance, @@ -390,34 +316,18 @@ function createGeminiImageTool(fields = {}) { throw new Error('This tool is only available for agents.'); } - // Skip validation during tool creation - validation happens at runtime in initializeGeminiClient - // This allows the tool to be added to agents when using Vertex AI without requiring API keys - // The actual credentials check happens when the tool is invoked - - const { - req, - imageFiles = [], - processFileURL, - userId, - fileStrategy, - GEMINI_API_KEY, - GOOGLE_KEY, - // GEMINI_VERTEX_ENABLED is used for auth validation only (not used in code) - // When set as env var, it signals Vertex AI is configured and bypasses API key requirement - } = fields; + const { req, imageFiles = [], userId, fileStrategy, GEMINI_API_KEY, GOOGLE_KEY } = fields; const imageOutputType = fields.imageOutputType || EImageOutputType.PNG; const geminiImageGenTool = tool( - async ({ prompt, image_ids, aspectRatio, imageSize }, _runnableConfig) => { + async ({ prompt, image_ids, aspectRatio, imageSize }, runnableConfig) => { if (!prompt) { throw new Error('Missing required field: prompt'); } - logger.debug('[GeminiImageGen] Generating image with prompt:', prompt?.substring(0, 100)); - logger.debug('[GeminiImageGen] Options:', { aspectRatio, imageSize }); + logger.debug('[GeminiImageGen] Generating image', { aspectRatio, imageSize }); - // Initialize Gemini client with user-provided credentials let ai; try { ai = await initializeGeminiClient({ @@ -432,10 +342,8 @@ function createGeminiImageTool(fields = {}) { ]; } - // Build request contents const contents = [{ text: replaceUnwantedChars(prompt) }]; - // Add context images if provided if (image_ids?.length > 0) { const contextImages = await convertImagesToInlineData({ imageFiles, @@ -447,28 +355,34 @@ function createGeminiImageTool(fields = {}) { logger.debug('[GeminiImageGen] Added', contextImages.length, 'context images'); } - // Generate image let apiResponse; const geminiModel = process.env.GEMINI_IMAGE_MODEL || 'gemini-2.5-flash-image'; - try { - // Build config with optional imageConfig - const config = { - responseModalities: ['TEXT', 'IMAGE'], - }; + const config = { + responseModalities: ['TEXT', 'IMAGE'], + }; - // Add imageConfig if aspectRatio or imageSize is specified - // Note: gemini-2.5-flash-image doesn't support imageSize - const supportsImageSize = !geminiModel.includes('gemini-2.5-flash-image'); - if (aspectRatio || (imageSize && supportsImageSize)) { - config.imageConfig = {}; - if (aspectRatio) { - config.imageConfig.aspectRatio = aspectRatio; - } - if (imageSize && supportsImageSize) { - config.imageConfig.imageSize = imageSize; - } + const supportsImageSize = !geminiModel.includes('gemini-2.5-flash-image'); + if (aspectRatio || (imageSize && supportsImageSize)) { + config.imageConfig = {}; + if (aspectRatio) { + config.imageConfig.aspectRatio = aspectRatio; } + if (imageSize && supportsImageSize) { + config.imageConfig.imageSize = imageSize; + } + } + let derivedSignal = null; + let abortHandler = null; + + if (runnableConfig?.signal) { + derivedSignal = AbortSignal.any([runnableConfig.signal]); + abortHandler = () => logger.debug('[GeminiImageGen] Image generation aborted'); + derivedSignal.addEventListener('abort', abortHandler, { once: true }); + config.abortSignal = derivedSignal; + } + + try { apiResponse = await ai.models.generateContent({ model: geminiModel, contents, @@ -480,9 +394,12 @@ function createGeminiImageTool(fields = {}) { [{ type: ContentTypes.TEXT, text: `Image generation failed: ${error.message}` }], { content: [], file_ids: [] }, ]; + } finally { + if (abortHandler && derivedSignal) { + derivedSignal.removeEventListener('abort', abortHandler); + } } - // Check for safety blocks const safetyBlock = checkForSafetyBlock(apiResponse); if (safetyBlock) { logger.warn('[GeminiImageGen] Safety block:', safetyBlock); @@ -509,46 +426,7 @@ function createGeminiImageTool(fields = {}) { const imageData = convertedBuffer.toString('base64'); const mimeType = outputFormat === 'jpeg' ? 'image/jpeg' : `image/${outputFormat}`; - logger.debug('[GeminiImageGen] Image format:', { outputFormat, mimeType }); - - let imageUrl; - const useLocalStorage = !fileStrategy || fileStrategy === FileSources.local; - - if (useLocalStorage) { - try { - imageUrl = await saveImageLocally(imageData, outputFormat, userId); - } catch (error) { - logger.error('[GeminiImageGen] Local save failed:', error); - imageUrl = `data:${mimeType};base64,${imageData}`; - } - } else { - const cloudUrl = await saveToCloudStorage({ - base64Data: imageData, - format: outputFormat, - processFileURL, - fileStrategy, - userId, - }); - - if (cloudUrl) { - imageUrl = cloudUrl; - } else { - // Fallback to local - try { - imageUrl = await saveImageLocally(imageData, outputFormat, userId); - } catch (_error) { - imageUrl = `data:${mimeType};base64,${imageData}`; - } - } - } - - logger.debug('[GeminiImageGen] Image URL:', imageUrl); - - // For the artifact, we need a data URL (same as OpenAI) - // The local file save is for persistence, but the response needs a data URL const dataUrl = `data:${mimeType};base64,${imageData}`; - - // Return in content_and_artifact format (same as OpenAI) const file_ids = [v4()]; const content = [ { @@ -567,12 +445,15 @@ function createGeminiImageTool(fields = {}) { }, ]; - // Record token usage for balance tracking (don't await to avoid blocking response) - const conversationId = _runnableConfig?.configurable?.thread_id; + const conversationId = runnableConfig?.configurable?.thread_id; + const messageId = + runnableConfig?.configurable?.run_id ?? + runnableConfig?.configurable?.requestBody?.messageId; recordTokenUsage({ usageMetadata: apiResponse.usageMetadata, req, userId, + messageId, conversationId, model: geminiModel, }).catch((error) => { diff --git a/api/app/clients/tools/structured/specs/DALLE3-proxy.spec.js b/api/app/clients/tools/structured/specs/DALLE3-proxy.spec.js index 4481a7d70f..262842b3c2 100644 --- a/api/app/clients/tools/structured/specs/DALLE3-proxy.spec.js +++ b/api/app/clients/tools/structured/specs/DALLE3-proxy.spec.js @@ -1,7 +1,6 @@ const DALLE3 = require('../DALLE3'); const { ProxyAgent } = require('undici'); -jest.mock('tiktoken'); const processFileURL = jest.fn(); describe('DALLE3 Proxy Configuration', () => { diff --git a/api/app/clients/tools/structured/specs/DALLE3.spec.js b/api/app/clients/tools/structured/specs/DALLE3.spec.js index d2040989f9..6071929bfc 100644 --- a/api/app/clients/tools/structured/specs/DALLE3.spec.js +++ b/api/app/clients/tools/structured/specs/DALLE3.spec.js @@ -14,15 +14,6 @@ jest.mock('@librechat/data-schemas', () => { }; }); -jest.mock('tiktoken', () => { - return { - encoding_for_model: jest.fn().mockReturnValue({ - encode: jest.fn(), - decode: jest.fn(), - }), - }; -}); - const processFileURL = jest.fn(); const generate = jest.fn(); diff --git a/api/app/clients/tools/util/handleTools.js b/api/app/clients/tools/util/handleTools.js index 65c88ce83f..4b86101425 100644 --- a/api/app/clients/tools/util/handleTools.js +++ b/api/app/clients/tools/util/handleTools.js @@ -7,6 +7,7 @@ const { } = require('@librechat/agents'); const { checkAccess, + toolkitParent, createSafeUser, mcpToolPattern, loadWebSearchAuth, @@ -44,7 +45,7 @@ const { getUserPluginAuthValue } = require('~/server/services/PluginService'); const { createMCPTool, createMCPTools } = require('~/server/services/MCP'); const { loadAuthValues } = require('~/server/services/Tools/credentials'); const { getMCPServerTools } = require('~/server/services/Config'); -const { getRoleByName } = require('~/models/Role'); +const { getRoleByName } = require('~/models'); /** * Validates the availability and authentication of tools for a user based on environment variables or user-specific plugin authentication values. @@ -207,7 +208,7 @@ const loadTools = async ({ }, gemini_image_gen: async (toolContextMap) => { const authFields = getAuthFields('gemini_image_gen'); - const authValues = await loadAuthValues({ userId: user, authFields }); + const authValues = await loadAuthValues({ userId: user, authFields, throwError: false }); const imageFiles = options.tool_resources?.[EToolResources.image_edit]?.files ?? []; const toolContext = buildImageToolContext({ imageFiles, @@ -222,7 +223,6 @@ const loadTools = async ({ isAgent: !!agent, req: options.req, imageFiles, - processFileURL: options.processFileURL, userId: user, fileStrategy, }); @@ -370,8 +370,16 @@ const loadTools = async ({ continue; } - if (customConstructors[tool]) { - requestedTools[tool] = async () => customConstructors[tool](toolContextMap); + const toolKey = customConstructors[tool] ? tool : toolkitParent[tool]; + if (toolKey && customConstructors[toolKey]) { + if (!requestedTools[toolKey]) { + let cached; + requestedTools[toolKey] = async () => { + cached ??= customConstructors[toolKey](toolContextMap); + return cached; + }; + } + requestedTools[tool] = requestedTools[toolKey]; continue; } diff --git a/api/cache/getLogStores.js b/api/cache/getLogStores.js index 3089192196..70eb681e53 100644 --- a/api/cache/getLogStores.js +++ b/api/cache/getLogStores.js @@ -47,7 +47,7 @@ const namespaces = { [CacheKeys.MODEL_QUERIES]: standardCache(CacheKeys.MODEL_QUERIES), [CacheKeys.AUDIO_RUNS]: standardCache(CacheKeys.AUDIO_RUNS, Time.TEN_MINUTES), [CacheKeys.MESSAGES]: standardCache(CacheKeys.MESSAGES, Time.ONE_MINUTE), - [CacheKeys.FLOWS]: standardCache(CacheKeys.FLOWS, Time.ONE_MINUTE * 3), + [CacheKeys.FLOWS]: standardCache(CacheKeys.FLOWS, Time.ONE_MINUTE * 10), [CacheKeys.OPENID_EXCHANGED_TOKENS]: standardCache( CacheKeys.OPENID_EXCHANGED_TOKENS, Time.TEN_MINUTES, diff --git a/api/db/connect.js b/api/db/connect.js index 26166ccff8..3534884b57 100644 --- a/api/db/connect.js +++ b/api/db/connect.js @@ -40,6 +40,10 @@ if (!cached) { cached = global.mongoose = { conn: null, promise: null }; } +mongoose.connection.on('error', (err) => { + logger.error('[connectDb] MongoDB connection error:', err); +}); + async function connectDb() { if (cached.conn && cached.conn?._readyState === 1) { return cached.conn; diff --git a/api/db/indexSync.js b/api/db/indexSync.js index 8e8e999d92..130cde77b8 100644 --- a/api/db/indexSync.js +++ b/api/db/indexSync.js @@ -236,8 +236,12 @@ async function performSync(flowManager, flowId, flowType) { const messageCount = messageProgress.totalDocuments; const messagesIndexed = messageProgress.totalProcessed; const unindexedMessages = messageCount - messagesIndexed; + const noneIndexed = messagesIndexed === 0 && unindexedMessages > 0; - if (settingsUpdated || unindexedMessages > syncThreshold) { + if (settingsUpdated || noneIndexed || unindexedMessages > syncThreshold) { + if (noneIndexed && !settingsUpdated) { + logger.info('[indexSync] No messages marked as indexed, forcing full sync'); + } logger.info(`[indexSync] Starting message sync (${unindexedMessages} unindexed)`); await Message.syncWithMeili(); messagesSync = true; @@ -261,9 +265,13 @@ async function performSync(flowManager, flowId, flowType) { const convoCount = convoProgress.totalDocuments; const convosIndexed = convoProgress.totalProcessed; - const unindexedConvos = convoCount - convosIndexed; - if (settingsUpdated || unindexedConvos > syncThreshold) { + const noneConvosIndexed = convosIndexed === 0 && unindexedConvos > 0; + + if (settingsUpdated || noneConvosIndexed || unindexedConvos > syncThreshold) { + if (noneConvosIndexed && !settingsUpdated) { + logger.info('[indexSync] No conversations marked as indexed, forcing full sync'); + } logger.info(`[indexSync] Starting convos sync (${unindexedConvos} unindexed)`); await Conversation.syncWithMeili(); convosSync = true; diff --git a/api/db/indexSync.spec.js b/api/db/indexSync.spec.js index c2e5901d6a..dbe07c7595 100644 --- a/api/db/indexSync.spec.js +++ b/api/db/indexSync.spec.js @@ -462,4 +462,69 @@ describe('performSync() - syncThreshold logic', () => { ); expect(mockLogger.info).toHaveBeenCalledWith('[indexSync] Starting convos sync (50 unindexed)'); }); + + test('forces sync when zero documents indexed (reset scenario) even if below threshold', async () => { + Message.getSyncProgress.mockResolvedValue({ + totalProcessed: 0, + totalDocuments: 680, + isComplete: false, + }); + + Conversation.getSyncProgress.mockResolvedValue({ + totalProcessed: 0, + totalDocuments: 76, + isComplete: false, + }); + + Message.syncWithMeili.mockResolvedValue(undefined); + Conversation.syncWithMeili.mockResolvedValue(undefined); + + const indexSync = require('./indexSync'); + await indexSync(); + + expect(Message.syncWithMeili).toHaveBeenCalledTimes(1); + expect(Conversation.syncWithMeili).toHaveBeenCalledTimes(1); + expect(mockLogger.info).toHaveBeenCalledWith( + '[indexSync] No messages marked as indexed, forcing full sync', + ); + expect(mockLogger.info).toHaveBeenCalledWith( + '[indexSync] Starting message sync (680 unindexed)', + ); + expect(mockLogger.info).toHaveBeenCalledWith( + '[indexSync] No conversations marked as indexed, forcing full sync', + ); + expect(mockLogger.info).toHaveBeenCalledWith('[indexSync] Starting convos sync (76 unindexed)'); + }); + + test('does NOT force sync when some documents already indexed and below threshold', async () => { + Message.getSyncProgress.mockResolvedValue({ + totalProcessed: 630, + totalDocuments: 680, + isComplete: false, + }); + + Conversation.getSyncProgress.mockResolvedValue({ + totalProcessed: 70, + totalDocuments: 76, + isComplete: false, + }); + + const indexSync = require('./indexSync'); + await indexSync(); + + expect(Message.syncWithMeili).not.toHaveBeenCalled(); + expect(Conversation.syncWithMeili).not.toHaveBeenCalled(); + expect(mockLogger.info).not.toHaveBeenCalledWith( + '[indexSync] No messages marked as indexed, forcing full sync', + ); + expect(mockLogger.info).not.toHaveBeenCalledWith( + '[indexSync] No conversations marked as indexed, forcing full sync', + ); + expect(mockLogger.info).toHaveBeenCalledWith( + '[indexSync] 50 messages unindexed (below threshold: 1000, skipping)', + ); + expect(mockLogger.info).toHaveBeenCalledWith( + '[indexSync] 6 convos unindexed (below threshold: 1000, skipping)', + ); + }); }); diff --git a/api/jest.config.js b/api/jest.config.js index 20ee3c6aed..47f8b7287b 100644 --- a/api/jest.config.js +++ b/api/jest.config.js @@ -3,12 +3,13 @@ module.exports = { clearMocks: true, roots: [''], coverageDirectory: 'coverage', + maxWorkers: '50%', testTimeout: 30000, // 30 seconds timeout for all tests setupFiles: ['./test/jestSetup.js', './test/__mocks__/logger.js'], moduleNameMapper: { '~/(.*)': '/$1', '~/data/auth.json': '/__mocks__/auth.mock.json', - '^openid-client/passport$': '/test/__mocks__/openid-client-passport.js', // Mock for the passport strategy part + '^openid-client/passport$': '/test/__mocks__/openid-client-passport.js', '^openid-client$': '/test/__mocks__/openid-client.js', }, transformIgnorePatterns: ['/node_modules/(?!(openid-client|oauth4webapi|jose)/).*/'], diff --git a/api/models/Action.js b/api/models/Action.js deleted file mode 100644 index 20aa20a7e4..0000000000 --- a/api/models/Action.js +++ /dev/null @@ -1,77 +0,0 @@ -const { Action } = require('~/db/models'); - -/** - * Update an action with new data without overwriting existing properties, - * or create a new action if it doesn't exist. - * - * @param {Object} searchParams - The search parameters to find the action to update. - * @param {string} searchParams.action_id - The ID of the action to update. - * @param {string} searchParams.user - The user ID of the action's author. - * @param {Object} updateData - An object containing the properties to update. - * @returns {Promise} The updated or newly created action document as a plain object. - */ -const updateAction = async (searchParams, updateData) => { - const options = { new: true, upsert: true }; - return await Action.findOneAndUpdate(searchParams, updateData, options).lean(); -}; - -/** - * Retrieves all actions that match the given search parameters. - * - * @param {Object} searchParams - The search parameters to find matching actions. - * @param {boolean} includeSensitive - Flag to include sensitive data in the metadata. - * @returns {Promise>} A promise that resolves to an array of action documents as plain objects. - */ -const getActions = async (searchParams, includeSensitive = false) => { - const actions = await Action.find(searchParams).lean(); - - if (!includeSensitive) { - for (let i = 0; i < actions.length; i++) { - const metadata = actions[i].metadata; - if (!metadata) { - continue; - } - - const sensitiveFields = ['api_key', 'oauth_client_id', 'oauth_client_secret']; - for (let field of sensitiveFields) { - if (metadata[field]) { - delete metadata[field]; - } - } - } - } - - return actions; -}; - -/** - * Deletes an action by params. - * - * @param {Object} searchParams - The search parameters to find the action to delete. - * @param {string} searchParams.action_id - The ID of the action to delete. - * @param {string} searchParams.user - The user ID of the action's author. - * @returns {Promise} A promise that resolves to the deleted action document as a plain object, or null if no document was found. - */ -const deleteAction = async (searchParams) => { - return await Action.findOneAndDelete(searchParams).lean(); -}; - -/** - * Deletes actions by params. - * - * @param {Object} searchParams - The search parameters to find the actions to delete. - * @param {string} searchParams.action_id - The ID of the action(s) to delete. - * @param {string} searchParams.user - The user ID of the action's author. - * @returns {Promise} A promise that resolves to the number of deleted action documents. - */ -const deleteActions = async (searchParams) => { - const result = await Action.deleteMany(searchParams); - return result.deletedCount; -}; - -module.exports = { - getActions, - updateAction, - deleteAction, - deleteActions, -}; diff --git a/api/models/Agent.js b/api/models/Agent.js deleted file mode 100644 index 663285183a..0000000000 --- a/api/models/Agent.js +++ /dev/null @@ -1,931 +0,0 @@ -const mongoose = require('mongoose'); -const crypto = require('node:crypto'); -const { logger } = require('@librechat/data-schemas'); -const { getCustomEndpointConfig } = require('@librechat/api'); -const { - Tools, - SystemRoles, - ResourceType, - actionDelimiter, - isAgentsEndpoint, - isEphemeralAgentId, - encodeEphemeralAgentId, -} = require('librechat-data-provider'); -const { mcp_all, mcp_delimiter } = require('librechat-data-provider').Constants; -const { - removeAgentFromAllProjects, - removeAgentIdsFromProject, - addAgentIdsToProject, -} = require('./Project'); -const { removeAllPermissions } = require('~/server/services/PermissionService'); -const { getMCPServerTools } = require('~/server/services/Config'); -const { Agent, AclEntry, User } = require('~/db/models'); -const { getActions } = require('./Action'); - -/** - * Extracts unique MCP server names from tools array - * Tools format: "toolName_mcp_serverName" or "sys__server__sys_mcp_serverName" - * @param {string[]} tools - Array of tool identifiers - * @returns {string[]} Array of unique MCP server names - */ -const extractMCPServerNames = (tools) => { - if (!tools || !Array.isArray(tools)) { - return []; - } - const serverNames = new Set(); - for (const tool of tools) { - if (!tool || !tool.includes(mcp_delimiter)) { - continue; - } - const parts = tool.split(mcp_delimiter); - if (parts.length >= 2) { - serverNames.add(parts[parts.length - 1]); - } - } - return Array.from(serverNames); -}; - -/** - * Create an agent with the provided data. - * @param {Object} agentData - The agent data to create. - * @returns {Promise} The created agent document as a plain object. - * @throws {Error} If the agent creation fails. - */ -const createAgent = async (agentData) => { - const { author: _author, ...versionData } = agentData; - const timestamp = new Date(); - const initialAgentData = { - ...agentData, - versions: [ - { - ...versionData, - createdAt: timestamp, - updatedAt: timestamp, - }, - ], - category: agentData.category || 'general', - mcpServerNames: extractMCPServerNames(agentData.tools), - }; - - return (await Agent.create(initialAgentData)).toObject(); -}; - -/** - * Get an agent document based on the provided ID. - * - * @param {Object} searchParameter - The search parameters to find the agent to update. - * @param {string} searchParameter.id - The ID of the agent to update. - * @param {string} searchParameter.author - The user ID of the agent's author. - * @returns {Promise} The agent document as a plain object, or null if not found. - */ -const getAgent = async (searchParameter) => await Agent.findOne(searchParameter).lean(); - -/** - * Get multiple agent documents based on the provided search parameters. - * - * @param {Object} searchParameter - The search parameters to find agents. - * @returns {Promise} Array of agent documents as plain objects. - */ -const getAgents = async (searchParameter) => await Agent.find(searchParameter).lean(); - -/** - * Load an agent based on the provided ID - * - * @param {Object} params - * @param {ServerRequest} params.req - * @param {string} params.spec - * @param {string} params.agent_id - * @param {string} params.endpoint - * @param {import('@librechat/agents').ClientOptions} [params.model_parameters] - * @returns {Promise} The agent document as a plain object, or null if not found. - */ -const loadEphemeralAgent = async ({ req, spec, endpoint, model_parameters: _m }) => { - const { model, ...model_parameters } = _m; - const modelSpecs = req.config?.modelSpecs?.list; - /** @type {TModelSpec | null} */ - let modelSpec = null; - if (spec != null && spec !== '') { - modelSpec = modelSpecs?.find((s) => s.name === spec) || null; - } - /** @type {TEphemeralAgent | null} */ - const ephemeralAgent = req.body.ephemeralAgent; - const mcpServers = new Set(ephemeralAgent?.mcp); - const userId = req.user?.id; // note: userId cannot be undefined at runtime - if (modelSpec?.mcpServers) { - for (const mcpServer of modelSpec.mcpServers) { - mcpServers.add(mcpServer); - } - } - /** @type {string[]} */ - const tools = []; - if (ephemeralAgent?.execute_code === true || modelSpec?.executeCode === true) { - tools.push(Tools.execute_code); - } - if (ephemeralAgent?.file_search === true || modelSpec?.fileSearch === true) { - tools.push(Tools.file_search); - } - if (ephemeralAgent?.web_search === true || modelSpec?.webSearch === true) { - tools.push(Tools.web_search); - } - - const addedServers = new Set(); - if (mcpServers.size > 0) { - for (const mcpServer of mcpServers) { - if (addedServers.has(mcpServer)) { - continue; - } - const serverTools = await getMCPServerTools(userId, mcpServer); - if (!serverTools) { - tools.push(`${mcp_all}${mcp_delimiter}${mcpServer}`); - addedServers.add(mcpServer); - continue; - } - tools.push(...Object.keys(serverTools)); - addedServers.add(mcpServer); - } - } - - const instructions = req.body.promptPrefix; - - // Get endpoint config for modelDisplayLabel fallback - const appConfig = req.config; - let endpointConfig = appConfig?.endpoints?.[endpoint]; - if (!isAgentsEndpoint(endpoint) && !endpointConfig) { - try { - endpointConfig = getCustomEndpointConfig({ endpoint, appConfig }); - } catch (err) { - logger.error('[loadEphemeralAgent] Error getting custom endpoint config', err); - } - } - - // For ephemeral agents, use modelLabel if provided, then model spec's label, - // then modelDisplayLabel from endpoint config, otherwise empty string to show model name - const sender = - model_parameters?.modelLabel ?? modelSpec?.label ?? endpointConfig?.modelDisplayLabel ?? ''; - - // Encode ephemeral agent ID with endpoint, model, and computed sender for display - const ephemeralId = encodeEphemeralAgentId({ endpoint, model, sender }); - - const result = { - id: ephemeralId, - instructions, - provider: endpoint, - model_parameters, - model, - tools, - }; - - if (ephemeralAgent?.artifacts != null && ephemeralAgent.artifacts) { - result.artifacts = ephemeralAgent.artifacts; - } - return result; -}; - -/** - * Load an agent based on the provided ID - * - * @param {Object} params - * @param {ServerRequest} params.req - * @param {string} params.spec - * @param {string} params.agent_id - * @param {string} params.endpoint - * @param {import('@librechat/agents').ClientOptions} [params.model_parameters] - * @returns {Promise} The agent document as a plain object, or null if not found. - */ -const loadAgent = async ({ req, spec, agent_id, endpoint, model_parameters }) => { - if (!agent_id) { - return null; - } - if (isEphemeralAgentId(agent_id)) { - return await loadEphemeralAgent({ req, spec, endpoint, model_parameters }); - } - const agent = await getAgent({ - id: agent_id, - }); - - if (!agent) { - return null; - } - - agent.version = agent.versions ? agent.versions.length : 0; - return agent; -}; - -/** - * Check if a version already exists in the versions array, excluding timestamp and author fields - * @param {Object} updateData - The update data to compare - * @param {Object} currentData - The current agent data - * @param {Array} versions - The existing versions array - * @param {string} [actionsHash] - Hash of current action metadata - * @returns {Object|null} - The matching version if found, null otherwise - */ -const isDuplicateVersion = (updateData, currentData, versions, actionsHash = null) => { - if (!versions || versions.length === 0) { - return null; - } - - const excludeFields = [ - '_id', - 'id', - 'createdAt', - 'updatedAt', - 'author', - 'updatedBy', - 'created_at', - 'updated_at', - '__v', - 'versions', - 'actionsHash', // Exclude actionsHash from direct comparison - ]; - - const { $push: _$push, $pull: _$pull, $addToSet: _$addToSet, ...directUpdates } = updateData; - - if (Object.keys(directUpdates).length === 0 && !actionsHash) { - return null; - } - - const wouldBeVersion = { ...currentData, ...directUpdates }; - const lastVersion = versions[versions.length - 1]; - - if (actionsHash && lastVersion.actionsHash !== actionsHash) { - return null; - } - - const allFields = new Set([...Object.keys(wouldBeVersion), ...Object.keys(lastVersion)]); - - const importantFields = Array.from(allFields).filter((field) => !excludeFields.includes(field)); - - let isMatch = true; - for (const field of importantFields) { - const wouldBeValue = wouldBeVersion[field]; - const lastVersionValue = lastVersion[field]; - - // Skip if both are undefined/null - if (!wouldBeValue && !lastVersionValue) { - continue; - } - - // Handle arrays - if (Array.isArray(wouldBeValue) || Array.isArray(lastVersionValue)) { - // Normalize: treat undefined/null as empty array for comparison - let wouldBeArr; - if (Array.isArray(wouldBeValue)) { - wouldBeArr = wouldBeValue; - } else if (wouldBeValue == null) { - wouldBeArr = []; - } else { - wouldBeArr = [wouldBeValue]; - } - - let lastVersionArr; - if (Array.isArray(lastVersionValue)) { - lastVersionArr = lastVersionValue; - } else if (lastVersionValue == null) { - lastVersionArr = []; - } else { - lastVersionArr = [lastVersionValue]; - } - - if (wouldBeArr.length !== lastVersionArr.length) { - isMatch = false; - break; - } - - // Special handling for projectIds (MongoDB ObjectIds) - if (field === 'projectIds') { - const wouldBeIds = wouldBeArr.map((id) => id.toString()).sort(); - const versionIds = lastVersionArr.map((id) => id.toString()).sort(); - - if (!wouldBeIds.every((id, i) => id === versionIds[i])) { - isMatch = false; - break; - } - } - // Handle arrays of objects - else if ( - wouldBeArr.length > 0 && - typeof wouldBeArr[0] === 'object' && - wouldBeArr[0] !== null - ) { - const sortedWouldBe = [...wouldBeArr].map((item) => JSON.stringify(item)).sort(); - const sortedVersion = [...lastVersionArr].map((item) => JSON.stringify(item)).sort(); - - if (!sortedWouldBe.every((item, i) => item === sortedVersion[i])) { - isMatch = false; - break; - } - } else { - const sortedWouldBe = [...wouldBeArr].sort(); - const sortedVersion = [...lastVersionArr].sort(); - - if (!sortedWouldBe.every((item, i) => item === sortedVersion[i])) { - isMatch = false; - break; - } - } - } - // Handle objects - else if (typeof wouldBeValue === 'object' && wouldBeValue !== null) { - const lastVersionObj = - typeof lastVersionValue === 'object' && lastVersionValue !== null ? lastVersionValue : {}; - - // For empty objects, normalize the comparison - const wouldBeKeys = Object.keys(wouldBeValue); - const lastVersionKeys = Object.keys(lastVersionObj); - - // If both are empty objects, they're equal - if (wouldBeKeys.length === 0 && lastVersionKeys.length === 0) { - continue; - } - - // Otherwise do a deep comparison - if (JSON.stringify(wouldBeValue) !== JSON.stringify(lastVersionObj)) { - isMatch = false; - break; - } - } - // Handle primitive values - else { - // For primitives, handle the case where one is undefined and the other is a default value - if (wouldBeValue !== lastVersionValue) { - // Special handling for boolean false vs undefined - if ( - typeof wouldBeValue === 'boolean' && - wouldBeValue === false && - lastVersionValue === undefined - ) { - continue; - } - // Special handling for empty string vs undefined - if ( - typeof wouldBeValue === 'string' && - wouldBeValue === '' && - lastVersionValue === undefined - ) { - continue; - } - isMatch = false; - break; - } - } - } - - return isMatch ? lastVersion : null; -}; - -/** - * Update an agent with new data without overwriting existing - * properties, or create a new agent if it doesn't exist. - * When an agent is updated, a copy of the current state will be saved to the versions array. - * - * @param {Object} searchParameter - The search parameters to find the agent to update. - * @param {string} searchParameter.id - The ID of the agent to update. - * @param {string} [searchParameter.author] - The user ID of the agent's author. - * @param {Object} updateData - An object containing the properties to update. - * @param {Object} [options] - Optional configuration object. - * @param {string} [options.updatingUserId] - The ID of the user performing the update (used for tracking non-author updates). - * @param {boolean} [options.forceVersion] - Force creation of a new version even if no fields changed. - * @param {boolean} [options.skipVersioning] - Skip version creation entirely (useful for isolated operations like sharing). - * @returns {Promise} The updated or newly created agent document as a plain object. - * @throws {Error} If the update would create a duplicate version - */ -const updateAgent = async (searchParameter, updateData, options = {}) => { - const { updatingUserId = null, forceVersion = false, skipVersioning = false } = options; - const mongoOptions = { new: true, upsert: false }; - - const currentAgent = await Agent.findOne(searchParameter); - if (currentAgent) { - const { - __v, - _id, - id: __id, - versions, - author: _author, - ...versionData - } = currentAgent.toObject(); - const { $push, $pull, $addToSet, ...directUpdates } = updateData; - - // Sync mcpServerNames when tools are updated - if (directUpdates.tools !== undefined) { - const mcpServerNames = extractMCPServerNames(directUpdates.tools); - directUpdates.mcpServerNames = mcpServerNames; - updateData.mcpServerNames = mcpServerNames; // Also update the original updateData - } - - let actionsHash = null; - - // Generate actions hash if agent has actions - if (currentAgent.actions && currentAgent.actions.length > 0) { - // Extract action IDs from the format "domain_action_id" - const actionIds = currentAgent.actions - .map((action) => { - const parts = action.split(actionDelimiter); - return parts[1]; // Get just the action ID part - }) - .filter(Boolean); - - if (actionIds.length > 0) { - try { - const actions = await getActions( - { - action_id: { $in: actionIds }, - }, - true, - ); // Include sensitive data for hash - - actionsHash = await generateActionMetadataHash(currentAgent.actions, actions); - } catch (error) { - logger.error('Error fetching actions for hash generation:', error); - } - } - } - - const shouldCreateVersion = - !skipVersioning && - (forceVersion || Object.keys(directUpdates).length > 0 || $push || $pull || $addToSet); - - if (shouldCreateVersion) { - const duplicateVersion = isDuplicateVersion(updateData, versionData, versions, actionsHash); - if (duplicateVersion && !forceVersion) { - // No changes detected, return the current agent without creating a new version - const agentObj = currentAgent.toObject(); - agentObj.version = versions.length; - return agentObj; - } - } - - const versionEntry = { - ...versionData, - ...directUpdates, - updatedAt: new Date(), - }; - - // Include actions hash in version if available - if (actionsHash) { - versionEntry.actionsHash = actionsHash; - } - - // Always store updatedBy field to track who made the change - if (updatingUserId) { - versionEntry.updatedBy = new mongoose.Types.ObjectId(updatingUserId); - } - - if (shouldCreateVersion) { - updateData.$push = { - ...($push || {}), - versions: versionEntry, - }; - } - } - - return Agent.findOneAndUpdate(searchParameter, updateData, mongoOptions).lean(); -}; - -/** - * Modifies an agent with the resource file id. - * @param {object} params - * @param {ServerRequest} params.req - * @param {string} params.agent_id - * @param {string} params.tool_resource - * @param {string} params.file_id - * @returns {Promise} The updated agent. - */ -const addAgentResourceFile = async ({ req, agent_id, tool_resource, file_id }) => { - const searchParameter = { id: agent_id }; - let agent = await getAgent(searchParameter); - if (!agent) { - throw new Error('Agent not found for adding resource file'); - } - const fileIdsPath = `tool_resources.${tool_resource}.file_ids`; - await Agent.updateOne( - { - id: agent_id, - [`${fileIdsPath}`]: { $exists: false }, - }, - { - $set: { - [`${fileIdsPath}`]: [], - }, - }, - ); - - const updateData = { - $addToSet: { - tools: tool_resource, - [fileIdsPath]: file_id, - }, - }; - - const updatedAgent = await updateAgent(searchParameter, updateData, { - updatingUserId: req?.user?.id, - }); - if (updatedAgent) { - return updatedAgent; - } else { - throw new Error('Agent not found for adding resource file'); - } -}; - -/** - * Removes multiple resource files from an agent using atomic operations. - * @param {object} params - * @param {string} params.agent_id - * @param {Array<{tool_resource: string, file_id: string}>} params.files - * @returns {Promise} The updated agent. - * @throws {Error} If the agent is not found or update fails. - */ -const removeAgentResourceFiles = async ({ agent_id, files }) => { - const searchParameter = { id: agent_id }; - - // Group files to remove by resource - const filesByResource = files.reduce((acc, { tool_resource, file_id }) => { - if (!acc[tool_resource]) { - acc[tool_resource] = []; - } - acc[tool_resource].push(file_id); - return acc; - }, {}); - - // Step 1: Atomically remove file IDs using $pull - const pullOps = {}; - const resourcesToCheck = new Set(); - for (const [resource, fileIds] of Object.entries(filesByResource)) { - const fileIdsPath = `tool_resources.${resource}.file_ids`; - pullOps[fileIdsPath] = { $in: fileIds }; - resourcesToCheck.add(resource); - } - - const updatePullData = { $pull: pullOps }; - const agentAfterPull = await Agent.findOneAndUpdate(searchParameter, updatePullData, { - new: true, - }).lean(); - - if (!agentAfterPull) { - // Agent might have been deleted concurrently, or never existed. - // Check if it existed before trying to throw. - const agentExists = await getAgent(searchParameter); - if (!agentExists) { - throw new Error('Agent not found for removing resource files'); - } - // If it existed but findOneAndUpdate returned null, something else went wrong. - throw new Error('Failed to update agent during file removal (pull step)'); - } - - // Return the agent state directly after the $pull operation. - // Skipping the $unset step for now to simplify and test core $pull atomicity. - // Empty arrays might remain, but the removal itself should be correct. - return agentAfterPull; -}; - -/** - * Deletes an agent based on the provided ID. - * - * @param {Object} searchParameter - The search parameters to find the agent to delete. - * @param {string} searchParameter.id - The ID of the agent to delete. - * @param {string} [searchParameter.author] - The user ID of the agent's author. - * @returns {Promise} Resolves when the agent has been successfully deleted. - */ -const deleteAgent = async (searchParameter) => { - const agent = await Agent.findOneAndDelete(searchParameter); - if (agent) { - await removeAgentFromAllProjects(agent.id); - await Promise.all([ - removeAllPermissions({ - resourceType: ResourceType.AGENT, - resourceId: agent._id, - }), - removeAllPermissions({ - resourceType: ResourceType.REMOTE_AGENT, - resourceId: agent._id, - }), - ]); - try { - await Agent.updateMany({ 'edges.to': agent.id }, { $pull: { edges: { to: agent.id } } }); - } catch (error) { - logger.error('[deleteAgent] Error removing agent from handoff edges', error); - } - try { - await User.updateMany( - { 'favorites.agentId': agent.id }, - { $pull: { favorites: { agentId: agent.id } } }, - ); - } catch (error) { - logger.error('[deleteAgent] Error removing agent from user favorites', error); - } - } - return agent; -}; - -/** - * Deletes all agents created by a specific user. - * @param {string} userId - The ID of the user whose agents should be deleted. - * @returns {Promise} A promise that resolves when all user agents have been deleted. - */ -const deleteUserAgents = async (userId) => { - try { - const userAgents = await getAgents({ author: userId }); - - if (userAgents.length === 0) { - return; - } - - const agentIds = userAgents.map((agent) => agent.id); - const agentObjectIds = userAgents.map((agent) => agent._id); - - for (const agentId of agentIds) { - await removeAgentFromAllProjects(agentId); - } - - await AclEntry.deleteMany({ - resourceType: { $in: [ResourceType.AGENT, ResourceType.REMOTE_AGENT] }, - resourceId: { $in: agentObjectIds }, - }); - - try { - await User.updateMany( - { 'favorites.agentId': { $in: agentIds } }, - { $pull: { favorites: { agentId: { $in: agentIds } } } }, - ); - } catch (error) { - logger.error('[deleteUserAgents] Error removing agents from user favorites', error); - } - - await Agent.deleteMany({ author: userId }); - } catch (error) { - logger.error('[deleteUserAgents] General error:', error); - } -}; - -/** - * Get agents by accessible IDs with optional cursor-based pagination. - * @param {Object} params - The parameters for getting accessible agents. - * @param {Array} [params.accessibleIds] - Array of agent ObjectIds the user has ACL access to. - * @param {Object} [params.otherParams] - Additional query parameters (including author filter). - * @param {number} [params.limit] - Number of agents to return (max 100). If not provided, returns all agents. - * @param {string} [params.after] - Cursor for pagination - get agents after this cursor. // base64 encoded JSON string with updatedAt and _id. - * @returns {Promise} A promise that resolves to an object containing the agents data and pagination info. - */ -const getListAgentsByAccess = async ({ - accessibleIds = [], - otherParams = {}, - limit = null, - after = null, -}) => { - const isPaginated = limit !== null && limit !== undefined; - const normalizedLimit = isPaginated ? Math.min(Math.max(1, parseInt(limit) || 20), 100) : null; - - // Build base query combining ACL accessible agents with other filters - const baseQuery = { ...otherParams, _id: { $in: accessibleIds } }; - - // Add cursor condition - if (after) { - try { - const cursor = JSON.parse(Buffer.from(after, 'base64').toString('utf8')); - const { updatedAt, _id } = cursor; - - const cursorCondition = { - $or: [ - { updatedAt: { $lt: new Date(updatedAt) } }, - { updatedAt: new Date(updatedAt), _id: { $gt: new mongoose.Types.ObjectId(_id) } }, - ], - }; - - // Merge cursor condition with base query - if (Object.keys(baseQuery).length > 0) { - baseQuery.$and = [{ ...baseQuery }, cursorCondition]; - // Remove the original conditions from baseQuery to avoid duplication - Object.keys(baseQuery).forEach((key) => { - if (key !== '$and') delete baseQuery[key]; - }); - } else { - Object.assign(baseQuery, cursorCondition); - } - } catch (error) { - logger.warn('Invalid cursor:', error.message); - } - } - - let query = Agent.find(baseQuery, { - id: 1, - _id: 1, - name: 1, - avatar: 1, - author: 1, - projectIds: 1, - description: 1, - updatedAt: 1, - category: 1, - support_contact: 1, - is_promoted: 1, - }).sort({ updatedAt: -1, _id: 1 }); - - // Only apply limit if pagination is requested - if (isPaginated) { - query = query.limit(normalizedLimit + 1); - } - - const agents = await query.lean(); - - const hasMore = isPaginated ? agents.length > normalizedLimit : false; - const data = (isPaginated ? agents.slice(0, normalizedLimit) : agents).map((agent) => { - if (agent.author) { - agent.author = agent.author.toString(); - } - return agent; - }); - - // Generate next cursor only if paginated - let nextCursor = null; - if (isPaginated && hasMore && data.length > 0) { - const lastAgent = agents[normalizedLimit - 1]; - nextCursor = Buffer.from( - JSON.stringify({ - updatedAt: lastAgent.updatedAt.toISOString(), - _id: lastAgent._id.toString(), - }), - ).toString('base64'); - } - - return { - object: 'list', - data, - first_id: data.length > 0 ? data[0].id : null, - last_id: data.length > 0 ? data[data.length - 1].id : null, - has_more: hasMore, - after: nextCursor, - }; -}; - -/** - * Updates the projects associated with an agent, adding and removing project IDs as specified. - * This function also updates the corresponding projects to include or exclude the agent ID. - * - * @param {Object} params - Parameters for updating the agent's projects. - * @param {IUser} params.user - Parameters for updating the agent's projects. - * @param {string} params.agentId - The ID of the agent to update. - * @param {string[]} [params.projectIds] - Array of project IDs to add to the agent. - * @param {string[]} [params.removeProjectIds] - Array of project IDs to remove from the agent. - * @returns {Promise} The updated agent document. - * @throws {Error} If there's an error updating the agent or projects. - */ -const updateAgentProjects = async ({ user, agentId, projectIds, removeProjectIds }) => { - const updateOps = {}; - - if (removeProjectIds && removeProjectIds.length > 0) { - for (const projectId of removeProjectIds) { - await removeAgentIdsFromProject(projectId, [agentId]); - } - updateOps.$pull = { projectIds: { $in: removeProjectIds } }; - } - - if (projectIds && projectIds.length > 0) { - for (const projectId of projectIds) { - await addAgentIdsToProject(projectId, [agentId]); - } - updateOps.$addToSet = { projectIds: { $each: projectIds } }; - } - - if (Object.keys(updateOps).length === 0) { - return await getAgent({ id: agentId }); - } - - const updateQuery = { id: agentId, author: user.id }; - if (user.role === SystemRoles.ADMIN) { - delete updateQuery.author; - } - - const updatedAgent = await updateAgent(updateQuery, updateOps, { - updatingUserId: user.id, - skipVersioning: true, - }); - if (updatedAgent) { - return updatedAgent; - } - if (updateOps.$addToSet) { - for (const projectId of projectIds) { - await removeAgentIdsFromProject(projectId, [agentId]); - } - } else if (updateOps.$pull) { - for (const projectId of removeProjectIds) { - await addAgentIdsToProject(projectId, [agentId]); - } - } - - return await getAgent({ id: agentId }); -}; - -/** - * Reverts an agent to a specific version in its version history. - * @param {Object} searchParameter - The search parameters to find the agent to revert. - * @param {string} searchParameter.id - The ID of the agent to revert. - * @param {string} [searchParameter.author] - The user ID of the agent's author. - * @param {number} versionIndex - The index of the version to revert to in the versions array. - * @returns {Promise} The updated agent document after reverting. - * @throws {Error} If the agent is not found or the specified version does not exist. - */ -const revertAgentVersion = async (searchParameter, versionIndex) => { - const agent = await Agent.findOne(searchParameter); - if (!agent) { - throw new Error('Agent not found'); - } - - if (!agent.versions || !agent.versions[versionIndex]) { - throw new Error(`Version ${versionIndex} not found`); - } - - const revertToVersion = agent.versions[versionIndex]; - - const updateData = { - ...revertToVersion, - }; - - delete updateData._id; - delete updateData.id; - delete updateData.versions; - delete updateData.author; - delete updateData.updatedBy; - - return Agent.findOneAndUpdate(searchParameter, updateData, { new: true }).lean(); -}; - -/** - * Generates a hash of action metadata for version comparison - * @param {string[]} actionIds - Array of action IDs in format "domain_action_id" - * @param {Action[]} actions - Array of action documents - * @returns {Promise} - SHA256 hash of the action metadata - */ -const generateActionMetadataHash = async (actionIds, actions) => { - if (!actionIds || actionIds.length === 0) { - return ''; - } - - // Create a map of action_id to metadata for quick lookup - const actionMap = new Map(); - actions.forEach((action) => { - actionMap.set(action.action_id, action.metadata); - }); - - // Sort action IDs for consistent hashing - const sortedActionIds = [...actionIds].sort(); - - // Build a deterministic string representation of all action metadata - const metadataString = sortedActionIds - .map((actionFullId) => { - // Extract just the action_id part (after the delimiter) - const parts = actionFullId.split(actionDelimiter); - const actionId = parts[1]; - - const metadata = actionMap.get(actionId); - if (!metadata) { - return `${actionId}:null`; - } - - // Sort metadata keys for deterministic output - const sortedKeys = Object.keys(metadata).sort(); - const metadataStr = sortedKeys - .map((key) => `${key}:${JSON.stringify(metadata[key])}`) - .join(','); - return `${actionId}:{${metadataStr}}`; - }) - .join(';'); - - // Use Web Crypto API to generate hash - const encoder = new TextEncoder(); - const data = encoder.encode(metadataString); - const hashBuffer = await crypto.webcrypto.subtle.digest('SHA-256', data); - const hashArray = Array.from(new Uint8Array(hashBuffer)); - const hashHex = hashArray.map((b) => b.toString(16).padStart(2, '0')).join(''); - - return hashHex; -}; -/** - * Counts the number of promoted agents. - * @returns {Promise} - The count of promoted agents - */ -const countPromotedAgents = async () => { - const count = await Agent.countDocuments({ is_promoted: true }); - return count; -}; - -/** - * Load a default agent based on the endpoint - * @param {string} endpoint - * @returns {Agent | null} - */ - -module.exports = { - getAgent, - getAgents, - loadAgent, - createAgent, - updateAgent, - deleteAgent, - deleteUserAgents, - revertAgentVersion, - updateAgentProjects, - countPromotedAgents, - addAgentResourceFile, - getListAgentsByAccess, - removeAgentResourceFiles, - generateActionMetadataHash, -}; diff --git a/api/models/Assistant.js b/api/models/Assistant.js deleted file mode 100644 index be94d35d7d..0000000000 --- a/api/models/Assistant.js +++ /dev/null @@ -1,62 +0,0 @@ -const { Assistant } = require('~/db/models'); - -/** - * Update an assistant with new data without overwriting existing properties, - * or create a new assistant if it doesn't exist. - * - * @param {Object} searchParams - The search parameters to find the assistant to update. - * @param {string} searchParams.assistant_id - The ID of the assistant to update. - * @param {string} searchParams.user - The user ID of the assistant's author. - * @param {Object} updateData - An object containing the properties to update. - * @returns {Promise} The updated or newly created assistant document as a plain object. - */ -const updateAssistantDoc = async (searchParams, updateData) => { - const options = { new: true, upsert: true }; - return await Assistant.findOneAndUpdate(searchParams, updateData, options).lean(); -}; - -/** - * Retrieves an assistant document based on the provided ID. - * - * @param {Object} searchParams - The search parameters to find the assistant to update. - * @param {string} searchParams.assistant_id - The ID of the assistant to update. - * @param {string} searchParams.user - The user ID of the assistant's author. - * @returns {Promise} The assistant document as a plain object, or null if not found. - */ -const getAssistant = async (searchParams) => await Assistant.findOne(searchParams).lean(); - -/** - * Retrieves all assistants that match the given search parameters. - * - * @param {Object} searchParams - The search parameters to find matching assistants. - * @param {Object} [select] - Optional. Specifies which document fields to include or exclude. - * @returns {Promise>} A promise that resolves to an array of assistant documents as plain objects. - */ -const getAssistants = async (searchParams, select = null) => { - let query = Assistant.find(searchParams); - - if (select) { - query = query.select(select); - } - - return await query.lean(); -}; - -/** - * Deletes an assistant based on the provided ID. - * - * @param {Object} searchParams - The search parameters to find the assistant to delete. - * @param {string} searchParams.assistant_id - The ID of the assistant to delete. - * @param {string} searchParams.user - The user ID of the assistant's author. - * @returns {Promise} Resolves when the assistant has been successfully deleted. - */ -const deleteAssistant = async (searchParams) => { - return await Assistant.findOneAndDelete(searchParams); -}; - -module.exports = { - updateAssistantDoc, - deleteAssistant, - getAssistants, - getAssistant, -}; diff --git a/api/models/Banner.js b/api/models/Banner.js deleted file mode 100644 index 42ad1599ed..0000000000 --- a/api/models/Banner.js +++ /dev/null @@ -1,28 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); -const { Banner } = require('~/db/models'); - -/** - * Retrieves the current active banner. - * @returns {Promise} The active banner object or null if no active banner is found. - */ -const getBanner = async (user) => { - try { - const now = new Date(); - const banner = await Banner.findOne({ - displayFrom: { $lte: now }, - $or: [{ displayTo: { $gte: now } }, { displayTo: null }], - type: 'banner', - }).lean(); - - if (!banner || banner.isPublic || user) { - return banner; - } - - return null; - } catch (error) { - logger.error('[getBanners] Error getting banners', error); - throw new Error('Error getting banners'); - } -}; - -module.exports = { getBanner }; diff --git a/api/models/Categories.js b/api/models/Categories.js deleted file mode 100644 index 34bd2d8ed2..0000000000 --- a/api/models/Categories.js +++ /dev/null @@ -1,57 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); - -const options = [ - { - label: 'com_ui_idea', - value: 'idea', - }, - { - label: 'com_ui_travel', - value: 'travel', - }, - { - label: 'com_ui_teach_or_explain', - value: 'teach_or_explain', - }, - { - label: 'com_ui_write', - value: 'write', - }, - { - label: 'com_ui_shop', - value: 'shop', - }, - { - label: 'com_ui_code', - value: 'code', - }, - { - label: 'com_ui_misc', - value: 'misc', - }, - { - label: 'com_ui_roleplay', - value: 'roleplay', - }, - { - label: 'com_ui_finance', - value: 'finance', - }, -]; - -module.exports = { - /** - * Retrieves the categories asynchronously. - * @returns {Promise} An array of category objects. - * @throws {Error} If there is an error retrieving the categories. - */ - getCategories: async () => { - try { - // const categories = await Categories.find(); - return options; - } catch (error) { - logger.error('Error getting categories', error); - return []; - } - }, -}; diff --git a/api/models/Conversation.js b/api/models/Conversation.js deleted file mode 100644 index a8f5f9a36c..0000000000 --- a/api/models/Conversation.js +++ /dev/null @@ -1,367 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); -const { createTempChatExpirationDate } = require('@librechat/api'); -const { getMessages, deleteMessages } = require('./Message'); -const { Conversation } = require('~/db/models'); - -/** - * Searches for a conversation by conversationId and returns a lean document with only conversationId and user. - * @param {string} conversationId - The conversation's ID. - * @returns {Promise<{conversationId: string, user: string} | null>} The conversation object with selected fields or null if not found. - */ -const searchConversation = async (conversationId) => { - try { - return await Conversation.findOne({ conversationId }, 'conversationId user').lean(); - } catch (error) { - logger.error('[searchConversation] Error searching conversation', error); - throw new Error('Error searching conversation'); - } -}; - -/** - * Retrieves a single conversation for a given user and conversation ID. - * @param {string} user - The user's ID. - * @param {string} conversationId - The conversation's ID. - * @returns {Promise} The conversation object. - */ -const getConvo = async (user, conversationId) => { - try { - return await Conversation.findOne({ user, conversationId }).lean(); - } catch (error) { - logger.error('[getConvo] Error getting single conversation', error); - throw new Error('Error getting single conversation'); - } -}; - -const deleteNullOrEmptyConversations = async () => { - try { - const filter = { - $or: [ - { conversationId: null }, - { conversationId: '' }, - { conversationId: { $exists: false } }, - ], - }; - - const result = await Conversation.deleteMany(filter); - - // Delete associated messages - const messageDeleteResult = await deleteMessages(filter); - - logger.info( - `[deleteNullOrEmptyConversations] Deleted ${result.deletedCount} conversations and ${messageDeleteResult.deletedCount} messages`, - ); - - return { - conversations: result, - messages: messageDeleteResult, - }; - } catch (error) { - logger.error('[deleteNullOrEmptyConversations] Error deleting conversations', error); - throw new Error('Error deleting conversations with null or empty conversationId'); - } -}; - -/** - * Searches for a conversation by conversationId and returns associated file ids. - * @param {string} conversationId - The conversation's ID. - * @returns {Promise} - */ -const getConvoFiles = async (conversationId) => { - try { - return (await Conversation.findOne({ conversationId }, 'files').lean())?.files ?? []; - } catch (error) { - logger.error('[getConvoFiles] Error getting conversation files', error); - throw new Error('Error getting conversation files'); - } -}; - -module.exports = { - getConvoFiles, - searchConversation, - deleteNullOrEmptyConversations, - /** - * Saves a conversation to the database. - * @param {Object} req - The request object. - * @param {string} conversationId - The conversation's ID. - * @param {Object} metadata - Additional metadata to log for operation. - * @returns {Promise} The conversation object. - */ - saveConvo: async (req, { conversationId, newConversationId, ...convo }, metadata) => { - try { - if (metadata?.context) { - logger.debug(`[saveConvo] ${metadata.context}`); - } - - const messages = await getMessages({ conversationId }, '_id'); - const update = { ...convo, messages, user: req.user.id }; - - if (newConversationId) { - update.conversationId = newConversationId; - } - - if (req?.body?.isTemporary) { - try { - const appConfig = req.config; - update.expiredAt = createTempChatExpirationDate(appConfig?.interfaceConfig); - } catch (err) { - logger.error('Error creating temporary chat expiration date:', err); - logger.info(`---\`saveConvo\` context: ${metadata?.context}`); - update.expiredAt = null; - } - } else { - update.expiredAt = null; - } - - /** @type {{ $set: Partial; $unset?: Record }} */ - const updateOperation = { $set: update }; - if (metadata && metadata.unsetFields && Object.keys(metadata.unsetFields).length > 0) { - updateOperation.$unset = metadata.unsetFields; - } - - /** Note: the resulting Model object is necessary for Meilisearch operations */ - const conversation = await Conversation.findOneAndUpdate( - { conversationId, user: req.user.id }, - updateOperation, - { - new: true, - upsert: true, - }, - ); - - return conversation.toObject(); - } catch (error) { - logger.error('[saveConvo] Error saving conversation', error); - if (metadata && metadata?.context) { - logger.info(`[saveConvo] ${metadata.context}`); - } - return { message: 'Error saving conversation' }; - } - }, - bulkSaveConvos: async (conversations) => { - try { - const bulkOps = conversations.map((convo) => ({ - updateOne: { - filter: { conversationId: convo.conversationId, user: convo.user }, - update: convo, - upsert: true, - timestamps: false, - }, - })); - - const result = await Conversation.bulkWrite(bulkOps); - return result; - } catch (error) { - logger.error('[bulkSaveConvos] Error saving conversations in bulk', error); - throw new Error('Failed to save conversations in bulk.'); - } - }, - getConvosByCursor: async ( - user, - { - cursor, - limit = 25, - isArchived = false, - tags, - search, - sortBy = 'updatedAt', - sortDirection = 'desc', - } = {}, - ) => { - const filters = [{ user }]; - if (isArchived) { - filters.push({ isArchived: true }); - } else { - filters.push({ $or: [{ isArchived: false }, { isArchived: { $exists: false } }] }); - } - - if (Array.isArray(tags) && tags.length > 0) { - filters.push({ tags: { $in: tags } }); - } - - filters.push({ $or: [{ expiredAt: null }, { expiredAt: { $exists: false } }] }); - - if (search) { - try { - const meiliResults = await Conversation.meiliSearch(search, { filter: `user = "${user}"` }); - const matchingIds = Array.isArray(meiliResults.hits) - ? meiliResults.hits.map((result) => result.conversationId) - : []; - if (!matchingIds.length) { - return { conversations: [], nextCursor: null }; - } - filters.push({ conversationId: { $in: matchingIds } }); - } catch (error) { - logger.error('[getConvosByCursor] Error during meiliSearch', error); - throw new Error('Error during meiliSearch'); - } - } - - const validSortFields = ['title', 'createdAt', 'updatedAt']; - if (!validSortFields.includes(sortBy)) { - throw new Error( - `Invalid sortBy field: ${sortBy}. Must be one of ${validSortFields.join(', ')}`, - ); - } - const finalSortBy = sortBy; - const finalSortDirection = sortDirection === 'asc' ? 'asc' : 'desc'; - - let cursorFilter = null; - if (cursor) { - try { - const decoded = JSON.parse(Buffer.from(cursor, 'base64').toString()); - const { primary, secondary } = decoded; - const primaryValue = finalSortBy === 'title' ? primary : new Date(primary); - const secondaryValue = new Date(secondary); - const op = finalSortDirection === 'asc' ? '$gt' : '$lt'; - - cursorFilter = { - $or: [ - { [finalSortBy]: { [op]: primaryValue } }, - { - [finalSortBy]: primaryValue, - updatedAt: { [op]: secondaryValue }, - }, - ], - }; - } catch (err) { - logger.warn('[getConvosByCursor] Invalid cursor format, starting from beginning'); - } - if (cursorFilter) { - filters.push(cursorFilter); - } - } - - const query = filters.length === 1 ? filters[0] : { $and: filters }; - - try { - const sortOrder = finalSortDirection === 'asc' ? 1 : -1; - const sortObj = { [finalSortBy]: sortOrder }; - - if (finalSortBy !== 'updatedAt') { - sortObj.updatedAt = sortOrder; - } - - const convos = await Conversation.find(query) - .select( - 'conversationId endpoint title createdAt updatedAt user model agent_id assistant_id spec iconURL', - ) - .sort(sortObj) - .limit(limit + 1) - .lean(); - - let nextCursor = null; - if (convos.length > limit) { - convos.pop(); // Remove extra item used to detect next page - // Create cursor from the last RETURNED item (not the popped one) - const lastReturned = convos[convos.length - 1]; - const primaryValue = lastReturned[finalSortBy]; - const primaryStr = finalSortBy === 'title' ? primaryValue : primaryValue.toISOString(); - const secondaryStr = lastReturned.updatedAt.toISOString(); - const composite = { primary: primaryStr, secondary: secondaryStr }; - nextCursor = Buffer.from(JSON.stringify(composite)).toString('base64'); - } - - return { conversations: convos, nextCursor }; - } catch (error) { - logger.error('[getConvosByCursor] Error getting conversations', error); - throw new Error('Error getting conversations'); - } - }, - getConvosQueried: async (user, convoIds, cursor = null, limit = 25) => { - try { - if (!convoIds?.length) { - return { conversations: [], nextCursor: null, convoMap: {} }; - } - - const conversationIds = convoIds.map((convo) => convo.conversationId); - - const results = await Conversation.find({ - user, - conversationId: { $in: conversationIds }, - $or: [{ expiredAt: { $exists: false } }, { expiredAt: null }], - }).lean(); - - results.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt)); - - let filtered = results; - if (cursor && cursor !== 'start') { - const cursorDate = new Date(cursor); - filtered = results.filter((convo) => new Date(convo.updatedAt) < cursorDate); - } - - const limited = filtered.slice(0, limit + 1); - let nextCursor = null; - if (limited.length > limit) { - limited.pop(); // Remove extra item used to detect next page - // Create cursor from the last RETURNED item (not the popped one) - nextCursor = limited[limited.length - 1].updatedAt.toISOString(); - } - - const convoMap = {}; - limited.forEach((convo) => { - convoMap[convo.conversationId] = convo; - }); - - return { conversations: limited, nextCursor, convoMap }; - } catch (error) { - logger.error('[getConvosQueried] Error getting conversations', error); - throw new Error('Error fetching conversations'); - } - }, - getConvo, - /* chore: this method is not properly error handled */ - getConvoTitle: async (user, conversationId) => { - try { - const convo = await getConvo(user, conversationId); - /* ChatGPT Browser was triggering error here due to convo being saved later */ - if (convo && !convo.title) { - return null; - } else { - // TypeError: Cannot read properties of null (reading 'title') - return convo?.title || 'New Chat'; - } - } catch (error) { - logger.error('[getConvoTitle] Error getting conversation title', error); - throw new Error('Error getting conversation title'); - } - }, - /** - * Asynchronously deletes conversations and associated messages for a given user and filter. - * - * @async - * @function - * @param {string|ObjectId} user - The user's ID. - * @param {Object} filter - Additional filter criteria for the conversations to be deleted. - * @returns {Promise<{ n: number, ok: number, deletedCount: number, messages: { n: number, ok: number, deletedCount: number } }>} - * An object containing the count of deleted conversations and associated messages. - * @throws {Error} Throws an error if there's an issue with the database operations. - * - * @example - * const user = 'someUserId'; - * const filter = { someField: 'someValue' }; - * const result = await deleteConvos(user, filter); - * logger.error(result); // { n: 5, ok: 1, deletedCount: 5, messages: { n: 10, ok: 1, deletedCount: 10 } } - */ - deleteConvos: async (user, filter) => { - try { - const userFilter = { ...filter, user }; - const conversations = await Conversation.find(userFilter).select('conversationId'); - const conversationIds = conversations.map((c) => c.conversationId); - - if (!conversationIds.length) { - throw new Error('Conversation not found or already deleted.'); - } - - const deleteConvoResult = await Conversation.deleteMany(userFilter); - - const deleteMessagesResult = await deleteMessages({ - conversationId: { $in: conversationIds }, - }); - - return { ...deleteConvoResult, messages: deleteMessagesResult }; - } catch (error) { - logger.error('[deleteConvos] Error deleting conversations and messages', error); - throw error; - } - }, -}; diff --git a/api/models/ConversationTag.js b/api/models/ConversationTag.js deleted file mode 100644 index 47a6c2bbf5..0000000000 --- a/api/models/ConversationTag.js +++ /dev/null @@ -1,284 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); -const { ConversationTag, Conversation } = require('~/db/models'); - -/** - * Retrieves all conversation tags for a user. - * @param {string} user - The user ID. - * @returns {Promise} An array of conversation tags. - */ -const getConversationTags = async (user) => { - try { - return await ConversationTag.find({ user }).sort({ position: 1 }).lean(); - } catch (error) { - logger.error('[getConversationTags] Error getting conversation tags', error); - throw new Error('Error getting conversation tags'); - } -}; - -/** - * Creates a new conversation tag. - * @param {string} user - The user ID. - * @param {Object} data - The tag data. - * @param {string} data.tag - The tag name. - * @param {string} [data.description] - The tag description. - * @param {boolean} [data.addToConversation] - Whether to add the tag to a conversation. - * @param {string} [data.conversationId] - The conversation ID to add the tag to. - * @returns {Promise} The created tag. - */ -const createConversationTag = async (user, data) => { - try { - const { tag, description, addToConversation, conversationId } = data; - - const existingTag = await ConversationTag.findOne({ user, tag }).lean(); - if (existingTag) { - return existingTag; - } - - const maxPosition = await ConversationTag.findOne({ user }).sort('-position').lean(); - const position = (maxPosition?.position || 0) + 1; - - const newTag = await ConversationTag.findOneAndUpdate( - { tag, user }, - { - tag, - user, - count: addToConversation ? 1 : 0, - position, - description, - $setOnInsert: { createdAt: new Date() }, - }, - { - new: true, - upsert: true, - lean: true, - }, - ); - - if (addToConversation && conversationId) { - await Conversation.findOneAndUpdate( - { user, conversationId }, - { $addToSet: { tags: tag } }, - { new: true }, - ); - } - - return newTag; - } catch (error) { - logger.error('[createConversationTag] Error creating conversation tag', error); - throw new Error('Error creating conversation tag'); - } -}; - -/** - * Updates an existing conversation tag. - * @param {string} user - The user ID. - * @param {string} oldTag - The current tag name. - * @param {Object} data - The updated tag data. - * @param {string} [data.tag] - The new tag name. - * @param {string} [data.description] - The updated description. - * @param {number} [data.position] - The new position. - * @returns {Promise} The updated tag. - */ -const updateConversationTag = async (user, oldTag, data) => { - try { - const { tag: newTag, description, position } = data; - - const existingTag = await ConversationTag.findOne({ user, tag: oldTag }).lean(); - if (!existingTag) { - return null; - } - - if (newTag && newTag !== oldTag) { - const tagAlreadyExists = await ConversationTag.findOne({ user, tag: newTag }).lean(); - if (tagAlreadyExists) { - throw new Error('Tag already exists'); - } - - await Conversation.updateMany({ user, tags: oldTag }, { $set: { 'tags.$': newTag } }); - } - - const updateData = {}; - if (newTag) { - updateData.tag = newTag; - } - if (description !== undefined) { - updateData.description = description; - } - if (position !== undefined) { - await adjustPositions(user, existingTag.position, position); - updateData.position = position; - } - - return await ConversationTag.findOneAndUpdate({ user, tag: oldTag }, updateData, { - new: true, - lean: true, - }); - } catch (error) { - logger.error('[updateConversationTag] Error updating conversation tag', error); - throw new Error('Error updating conversation tag'); - } -}; - -/** - * Adjusts positions of tags when a tag's position is changed. - * @param {string} user - The user ID. - * @param {number} oldPosition - The old position of the tag. - * @param {number} newPosition - The new position of the tag. - * @returns {Promise} - */ -const adjustPositions = async (user, oldPosition, newPosition) => { - if (oldPosition === newPosition) { - return; - } - - const update = oldPosition < newPosition ? { $inc: { position: -1 } } : { $inc: { position: 1 } }; - const position = - oldPosition < newPosition - ? { - $gt: Math.min(oldPosition, newPosition), - $lte: Math.max(oldPosition, newPosition), - } - : { - $gte: Math.min(oldPosition, newPosition), - $lt: Math.max(oldPosition, newPosition), - }; - - await ConversationTag.updateMany( - { - user, - position, - }, - update, - ); -}; - -/** - * Deletes a conversation tag. - * @param {string} user - The user ID. - * @param {string} tag - The tag to delete. - * @returns {Promise} The deleted tag. - */ -const deleteConversationTag = async (user, tag) => { - try { - const deletedTag = await ConversationTag.findOneAndDelete({ user, tag }).lean(); - if (!deletedTag) { - return null; - } - - await Conversation.updateMany({ user, tags: tag }, { $pull: { tags: tag } }); - - await ConversationTag.updateMany( - { user, position: { $gt: deletedTag.position } }, - { $inc: { position: -1 } }, - ); - - return deletedTag; - } catch (error) { - logger.error('[deleteConversationTag] Error deleting conversation tag', error); - throw new Error('Error deleting conversation tag'); - } -}; - -/** - * Updates tags for a specific conversation. - * @param {string} user - The user ID. - * @param {string} conversationId - The conversation ID. - * @param {string[]} tags - The new set of tags for the conversation. - * @returns {Promise} The updated list of tags for the conversation. - */ -const updateTagsForConversation = async (user, conversationId, tags) => { - try { - const conversation = await Conversation.findOne({ user, conversationId }).lean(); - if (!conversation) { - throw new Error('Conversation not found'); - } - - const oldTags = new Set(conversation.tags); - const newTags = new Set(tags); - - const addedTags = [...newTags].filter((tag) => !oldTags.has(tag)); - const removedTags = [...oldTags].filter((tag) => !newTags.has(tag)); - - const bulkOps = []; - - for (const tag of addedTags) { - bulkOps.push({ - updateOne: { - filter: { user, tag }, - update: { $inc: { count: 1 } }, - upsert: true, - }, - }); - } - - for (const tag of removedTags) { - bulkOps.push({ - updateOne: { - filter: { user, tag }, - update: { $inc: { count: -1 } }, - }, - }); - } - - if (bulkOps.length > 0) { - await ConversationTag.bulkWrite(bulkOps); - } - - const updatedConversation = ( - await Conversation.findOneAndUpdate( - { user, conversationId }, - { $set: { tags: [...newTags] } }, - { new: true }, - ) - ).toObject(); - - return updatedConversation.tags; - } catch (error) { - logger.error('[updateTagsForConversation] Error updating tags', error); - throw new Error('Error updating tags for conversation'); - } -}; - -/** - * Increments tag counts for existing tags only. - * @param {string} user - The user ID. - * @param {string[]} tags - Array of tag names to increment - * @returns {Promise} - */ -const bulkIncrementTagCounts = async (user, tags) => { - if (!tags || tags.length === 0) { - return; - } - - try { - const uniqueTags = [...new Set(tags.filter(Boolean))]; - if (uniqueTags.length === 0) { - return; - } - - const bulkOps = uniqueTags.map((tag) => ({ - updateOne: { - filter: { user, tag }, - update: { $inc: { count: 1 } }, - }, - })); - - const result = await ConversationTag.bulkWrite(bulkOps); - if (result && result.modifiedCount > 0) { - logger.debug( - `user: ${user} | Incremented tag counts - modified ${result.modifiedCount} tags`, - ); - } - } catch (error) { - logger.error('[bulkIncrementTagCounts] Error incrementing tag counts', error); - } -}; - -module.exports = { - getConversationTags, - createConversationTag, - updateConversationTag, - deleteConversationTag, - bulkIncrementTagCounts, - updateTagsForConversation, -}; diff --git a/api/models/File.js b/api/models/File.js deleted file mode 100644 index 1a01ef12f9..0000000000 --- a/api/models/File.js +++ /dev/null @@ -1,250 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); -const { EToolResources, FileContext } = require('librechat-data-provider'); -const { File } = require('~/db/models'); - -/** - * Finds a file by its file_id with additional query options. - * @param {string} file_id - The unique identifier of the file. - * @param {object} options - Query options for filtering, projection, etc. - * @returns {Promise} A promise that resolves to the file document or null. - */ -const findFileById = async (file_id, options = {}) => { - return await File.findOne({ file_id, ...options }).lean(); -}; - -/** - * Retrieves files matching a given filter, sorted by the most recently updated. - * @param {Object} filter - The filter criteria to apply. - * @param {Object} [_sortOptions] - Optional sort parameters. - * @param {Object|String} [selectFields={ text: 0 }] - Fields to include/exclude in the query results. - * Default excludes the 'text' field. - * @returns {Promise>} A promise that resolves to an array of file documents. - */ -const getFiles = async (filter, _sortOptions, selectFields = { text: 0 }) => { - const sortOptions = { updatedAt: -1, ..._sortOptions }; - return await File.find(filter).select(selectFields).sort(sortOptions).lean(); -}; - -/** - * Retrieves tool files (files that are embedded or have a fileIdentifier) from an array of file IDs. - * Note: execute_code files are handled separately by getCodeGeneratedFiles. - * @param {string[]} fileIds - Array of file_id strings to search for - * @param {Set} toolResourceSet - Optional filter for tool resources - * @returns {Promise>} Files that match the criteria - */ -const getToolFilesByIds = async (fileIds, toolResourceSet) => { - if (!fileIds || !fileIds.length || !toolResourceSet?.size) { - return []; - } - - try { - const orConditions = []; - - if (toolResourceSet.has(EToolResources.context)) { - orConditions.push({ text: { $exists: true, $ne: null }, context: FileContext.agents }); - } - if (toolResourceSet.has(EToolResources.file_search)) { - orConditions.push({ embedded: true }); - } - - if (orConditions.length === 0) { - return []; - } - - const filter = { - file_id: { $in: fileIds }, - context: { $ne: FileContext.execute_code }, // Exclude code-generated files - $or: orConditions, - }; - - const selectFields = { text: 0 }; - const sortOptions = { updatedAt: -1 }; - - return await getFiles(filter, sortOptions, selectFields); - } catch (error) { - logger.error('[getToolFilesByIds] Error retrieving tool files:', error); - throw new Error('Error retrieving tool files'); - } -}; - -/** - * Retrieves files generated by code execution for a given conversation. - * These files are stored locally with fileIdentifier metadata for code env re-upload. - * @param {string} conversationId - The conversation ID to search for - * @param {string[]} [messageIds] - Optional array of messageIds to filter by (for linear thread filtering) - * @returns {Promise>} Files generated by code execution in the conversation - */ -const getCodeGeneratedFiles = async (conversationId, messageIds) => { - if (!conversationId) { - return []; - } - - /** messageIds are required for proper thread filtering of code-generated files */ - if (!messageIds || messageIds.length === 0) { - return []; - } - - try { - const filter = { - conversationId, - context: FileContext.execute_code, - messageId: { $exists: true, $in: messageIds }, - 'metadata.fileIdentifier': { $exists: true }, - }; - - const selectFields = { text: 0 }; - const sortOptions = { createdAt: 1 }; - - return await getFiles(filter, sortOptions, selectFields); - } catch (error) { - logger.error('[getCodeGeneratedFiles] Error retrieving code generated files:', error); - return []; - } -}; - -/** - * Retrieves user-uploaded execute_code files (not code-generated) by their file IDs. - * These are files with fileIdentifier metadata but context is NOT execute_code (e.g., agents or message_attachment). - * File IDs should be collected from message.files arrays in the current thread. - * @param {string[]} fileIds - Array of file IDs to fetch (from message.files in the thread) - * @returns {Promise>} User-uploaded execute_code files - */ -const getUserCodeFiles = async (fileIds) => { - if (!fileIds || fileIds.length === 0) { - return []; - } - - try { - const filter = { - file_id: { $in: fileIds }, - context: { $ne: FileContext.execute_code }, - 'metadata.fileIdentifier': { $exists: true }, - }; - - const selectFields = { text: 0 }; - const sortOptions = { createdAt: 1 }; - - return await getFiles(filter, sortOptions, selectFields); - } catch (error) { - logger.error('[getUserCodeFiles] Error retrieving user code files:', error); - return []; - } -}; - -/** - * Creates a new file with a TTL of 1 hour. - * @param {MongoFile} data - The file data to be created, must contain file_id. - * @param {boolean} disableTTL - Whether to disable the TTL. - * @returns {Promise} A promise that resolves to the created file document. - */ -const createFile = async (data, disableTTL) => { - const fileData = { - ...data, - expiresAt: new Date(Date.now() + 3600 * 1000), - }; - - if (disableTTL) { - delete fileData.expiresAt; - } - - return await File.findOneAndUpdate({ file_id: data.file_id }, fileData, { - new: true, - upsert: true, - }).lean(); -}; - -/** - * Updates a file identified by file_id with new data and removes the TTL. - * @param {MongoFile} data - The data to update, must contain file_id. - * @returns {Promise} A promise that resolves to the updated file document. - */ -const updateFile = async (data) => { - const { file_id, ...update } = data; - const updateOperation = { - $set: update, - $unset: { expiresAt: '' }, // Remove the expiresAt field to prevent TTL - }; - return await File.findOneAndUpdate({ file_id }, updateOperation, { new: true }).lean(); -}; - -/** - * Increments the usage of a file identified by file_id. - * @param {MongoFile} data - The data to update, must contain file_id and the increment value for usage. - * @returns {Promise} A promise that resolves to the updated file document. - */ -const updateFileUsage = async (data) => { - const { file_id, inc = 1 } = data; - const updateOperation = { - $inc: { usage: inc }, - $unset: { expiresAt: '', temp_file_id: '' }, - }; - return await File.findOneAndUpdate({ file_id }, updateOperation, { new: true }).lean(); -}; - -/** - * Deletes a file identified by file_id. - * @param {string} file_id - The unique identifier of the file to delete. - * @returns {Promise} A promise that resolves to the deleted file document or null. - */ -const deleteFile = async (file_id) => { - return await File.findOneAndDelete({ file_id }).lean(); -}; - -/** - * Deletes a file identified by a filter. - * @param {object} filter - The filter criteria to apply. - * @returns {Promise} A promise that resolves to the deleted file document or null. - */ -const deleteFileByFilter = async (filter) => { - return await File.findOneAndDelete(filter).lean(); -}; - -/** - * Deletes multiple files identified by an array of file_ids. - * @param {Array} file_ids - The unique identifiers of the files to delete. - * @returns {Promise} A promise that resolves to the result of the deletion operation. - */ -const deleteFiles = async (file_ids, user) => { - let deleteQuery = { file_id: { $in: file_ids } }; - if (user) { - deleteQuery = { user: user }; - } - return await File.deleteMany(deleteQuery); -}; - -/** - * Batch updates files with new signed URLs in MongoDB - * - * @param {MongoFile[]} updates - Array of updates in the format { file_id, filepath } - * @returns {Promise} - */ -async function batchUpdateFiles(updates) { - if (!updates || updates.length === 0) { - return; - } - - const bulkOperations = updates.map((update) => ({ - updateOne: { - filter: { file_id: update.file_id }, - update: { $set: { filepath: update.filepath } }, - }, - })); - - const result = await File.bulkWrite(bulkOperations); - logger.info(`Updated ${result.modifiedCount} files with new S3 URLs`); -} - -module.exports = { - findFileById, - getFiles, - getToolFilesByIds, - getCodeGeneratedFiles, - getUserCodeFiles, - createFile, - updateFile, - updateFileUsage, - deleteFile, - deleteFiles, - deleteFileByFilter, - batchUpdateFiles, -}; diff --git a/api/models/File.spec.js b/api/models/File.spec.js deleted file mode 100644 index 2d4282cff7..0000000000 --- a/api/models/File.spec.js +++ /dev/null @@ -1,629 +0,0 @@ -const mongoose = require('mongoose'); -const { v4: uuidv4 } = require('uuid'); -const { MongoMemoryServer } = require('mongodb-memory-server'); -const { createModels, createMethods } = require('@librechat/data-schemas'); -const { - SystemRoles, - ResourceType, - AccessRoleIds, - PrincipalType, -} = require('librechat-data-provider'); -const { grantPermission } = require('~/server/services/PermissionService'); -const { createAgent } = require('./Agent'); - -let File; -let Agent; -let AclEntry; -let User; -let modelsToCleanup = []; -let methods; -let getFiles; -let createFile; -let seedDefaultRoles; - -describe('File Access Control', () => { - let mongoServer; - - beforeAll(async () => { - mongoServer = await MongoMemoryServer.create(); - const mongoUri = mongoServer.getUri(); - await mongoose.connect(mongoUri); - - // Initialize all models - const models = createModels(mongoose); - - // Track which models we're adding - modelsToCleanup = Object.keys(models); - - // Register models on mongoose.models so methods can access them - const dbModels = require('~/db/models'); - Object.assign(mongoose.models, dbModels); - - File = dbModels.File; - Agent = dbModels.Agent; - AclEntry = dbModels.AclEntry; - User = dbModels.User; - - // Create methods from data-schemas (includes file methods) - methods = createMethods(mongoose); - getFiles = methods.getFiles; - createFile = methods.createFile; - seedDefaultRoles = methods.seedDefaultRoles; - - // Seed default roles - await seedDefaultRoles(); - }); - - afterAll(async () => { - // Clean up all collections before disconnecting - const collections = mongoose.connection.collections; - for (const key in collections) { - await collections[key].deleteMany({}); - } - - // Clear only the models we added - for (const modelName of modelsToCleanup) { - if (mongoose.models[modelName]) { - delete mongoose.models[modelName]; - } - } - - await mongoose.disconnect(); - await mongoServer.stop(); - }); - - beforeEach(async () => { - await File.deleteMany({}); - await Agent.deleteMany({}); - await AclEntry.deleteMany({}); - await User.deleteMany({}); - // Don't delete AccessRole as they are seeded defaults needed for tests - }); - - describe('hasAccessToFilesViaAgent', () => { - it('should efficiently check access for multiple files at once', async () => { - const userId = new mongoose.Types.ObjectId(); - const authorId = new mongoose.Types.ObjectId(); - const agentId = uuidv4(); - const fileIds = [uuidv4(), uuidv4(), uuidv4(), uuidv4()]; - - // Create users - await User.create({ - _id: userId, - email: 'user@example.com', - emailVerified: true, - provider: 'local', - }); - - await User.create({ - _id: authorId, - email: 'author@example.com', - emailVerified: true, - provider: 'local', - }); - - // Create files - for (const fileId of fileIds) { - await createFile({ - user: authorId, - file_id: fileId, - filename: `file-${fileId}.txt`, - filepath: `/uploads/${fileId}`, - }); - } - - // Create agent with only first two files attached - const agent = await createAgent({ - id: agentId, - name: 'Test Agent', - author: authorId, - model: 'gpt-4', - provider: 'openai', - tool_resources: { - file_search: { - file_ids: [fileIds[0], fileIds[1]], - }, - }, - }); - - // Grant EDIT permission to user on the agent - await grantPermission({ - principalType: PrincipalType.USER, - principalId: userId, - resourceType: ResourceType.AGENT, - resourceId: agent._id, - accessRoleId: AccessRoleIds.AGENT_EDITOR, - grantedBy: authorId, - }); - - // Check access for all files - const { hasAccessToFilesViaAgent } = require('~/server/services/Files/permissions'); - const accessMap = await hasAccessToFilesViaAgent({ - userId: userId, - role: SystemRoles.USER, - fileIds, - agentId: agent.id, // Use agent.id which is the custom UUID - }); - - // Should have access only to the first two files - expect(accessMap.get(fileIds[0])).toBe(true); - expect(accessMap.get(fileIds[1])).toBe(true); - expect(accessMap.get(fileIds[2])).toBe(false); - expect(accessMap.get(fileIds[3])).toBe(false); - }); - - it('should grant access to all files when user is the agent author', async () => { - const authorId = new mongoose.Types.ObjectId(); - const agentId = uuidv4(); - const fileIds = [uuidv4(), uuidv4(), uuidv4()]; - - // Create author user - await User.create({ - _id: authorId, - email: 'author@example.com', - emailVerified: true, - provider: 'local', - }); - - // Create agent - await createAgent({ - id: agentId, - name: 'Test Agent', - author: authorId, - model: 'gpt-4', - provider: 'openai', - tool_resources: { - file_search: { - file_ids: [fileIds[0]], // Only one file attached - }, - }, - }); - - // Check access as the author - const { hasAccessToFilesViaAgent } = require('~/server/services/Files/permissions'); - const accessMap = await hasAccessToFilesViaAgent({ - userId: authorId, - role: SystemRoles.USER, - fileIds, - agentId, - }); - - // Author should have access to all files - expect(accessMap.get(fileIds[0])).toBe(true); - expect(accessMap.get(fileIds[1])).toBe(true); - expect(accessMap.get(fileIds[2])).toBe(true); - }); - - it('should handle non-existent agent gracefully', async () => { - const userId = new mongoose.Types.ObjectId(); - const fileIds = [uuidv4(), uuidv4()]; - - // Create user - await User.create({ - _id: userId, - email: 'user@example.com', - emailVerified: true, - provider: 'local', - }); - - const { hasAccessToFilesViaAgent } = require('~/server/services/Files/permissions'); - const accessMap = await hasAccessToFilesViaAgent({ - userId: userId, - role: SystemRoles.USER, - fileIds, - agentId: 'non-existent-agent', - }); - - // Should have no access to any files - expect(accessMap.get(fileIds[0])).toBe(false); - expect(accessMap.get(fileIds[1])).toBe(false); - }); - - it('should deny access when user only has VIEW permission and needs access for deletion', async () => { - const userId = new mongoose.Types.ObjectId(); - const authorId = new mongoose.Types.ObjectId(); - const agentId = uuidv4(); - const fileIds = [uuidv4(), uuidv4()]; - - // Create users - await User.create({ - _id: userId, - email: 'user@example.com', - emailVerified: true, - provider: 'local', - }); - - await User.create({ - _id: authorId, - email: 'author@example.com', - emailVerified: true, - provider: 'local', - }); - - // Create agent with files - const agent = await createAgent({ - id: agentId, - name: 'View-Only Agent', - author: authorId, - model: 'gpt-4', - provider: 'openai', - tool_resources: { - file_search: { - file_ids: fileIds, - }, - }, - }); - - // Grant only VIEW permission to user on the agent - await grantPermission({ - principalType: PrincipalType.USER, - principalId: userId, - resourceType: ResourceType.AGENT, - resourceId: agent._id, - accessRoleId: AccessRoleIds.AGENT_VIEWER, - grantedBy: authorId, - }); - - // Check access for files - const { hasAccessToFilesViaAgent } = require('~/server/services/Files/permissions'); - const accessMap = await hasAccessToFilesViaAgent({ - userId: userId, - role: SystemRoles.USER, - fileIds, - agentId, - isDelete: true, - }); - - // Should have no access to any files when only VIEW permission - expect(accessMap.get(fileIds[0])).toBe(false); - expect(accessMap.get(fileIds[1])).toBe(false); - }); - - it('should grant access when user has VIEW permission', async () => { - const userId = new mongoose.Types.ObjectId(); - const authorId = new mongoose.Types.ObjectId(); - const agentId = uuidv4(); - const fileIds = [uuidv4(), uuidv4()]; - - // Create users - await User.create({ - _id: userId, - email: 'user@example.com', - emailVerified: true, - provider: 'local', - }); - - await User.create({ - _id: authorId, - email: 'author@example.com', - emailVerified: true, - provider: 'local', - }); - - // Create agent with files - const agent = await createAgent({ - id: agentId, - name: 'View-Only Agent', - author: authorId, - model: 'gpt-4', - provider: 'openai', - tool_resources: { - file_search: { - file_ids: fileIds, - }, - }, - }); - - // Grant only VIEW permission to user on the agent - await grantPermission({ - principalType: PrincipalType.USER, - principalId: userId, - resourceType: ResourceType.AGENT, - resourceId: agent._id, - accessRoleId: AccessRoleIds.AGENT_VIEWER, - grantedBy: authorId, - }); - - // Check access for files - const { hasAccessToFilesViaAgent } = require('~/server/services/Files/permissions'); - const accessMap = await hasAccessToFilesViaAgent({ - userId: userId, - role: SystemRoles.USER, - fileIds, - agentId, - }); - - expect(accessMap.get(fileIds[0])).toBe(true); - expect(accessMap.get(fileIds[1])).toBe(true); - }); - }); - - describe('getFiles with agent access control', () => { - test('should return files owned by user and files accessible through agent', async () => { - const authorId = new mongoose.Types.ObjectId(); - const userId = new mongoose.Types.ObjectId(); - const agentId = `agent_${uuidv4()}`; - const ownedFileId = `file_${uuidv4()}`; - const sharedFileId = `file_${uuidv4()}`; - const inaccessibleFileId = `file_${uuidv4()}`; - - // Create users - await User.create({ - _id: userId, - email: 'user@example.com', - emailVerified: true, - provider: 'local', - }); - - await User.create({ - _id: authorId, - email: 'author@example.com', - emailVerified: true, - provider: 'local', - }); - - // Create agent with shared file - const agent = await createAgent({ - id: agentId, - name: 'Shared Agent', - provider: 'test', - model: 'test-model', - author: authorId, - tool_resources: { - file_search: { - file_ids: [sharedFileId], - }, - }, - }); - - // Grant EDIT permission to user on the agent - await grantPermission({ - principalType: PrincipalType.USER, - principalId: userId, - resourceType: ResourceType.AGENT, - resourceId: agent._id, - accessRoleId: AccessRoleIds.AGENT_EDITOR, - grantedBy: authorId, - }); - - // Create files - await createFile({ - file_id: ownedFileId, - user: userId, - filename: 'owned.txt', - filepath: '/uploads/owned.txt', - type: 'text/plain', - bytes: 100, - }); - - await createFile({ - file_id: sharedFileId, - user: authorId, - filename: 'shared.txt', - filepath: '/uploads/shared.txt', - type: 'text/plain', - bytes: 200, - embedded: true, - }); - - await createFile({ - file_id: inaccessibleFileId, - user: authorId, - filename: 'inaccessible.txt', - filepath: '/uploads/inaccessible.txt', - type: 'text/plain', - bytes: 300, - }); - - // Get all files first - const allFiles = await getFiles( - { file_id: { $in: [ownedFileId, sharedFileId, inaccessibleFileId] } }, - null, - { text: 0 }, - ); - - // Then filter by access control - const { filterFilesByAgentAccess } = require('~/server/services/Files/permissions'); - const files = await filterFilesByAgentAccess({ - files: allFiles, - userId: userId, - role: SystemRoles.USER, - agentId, - }); - - expect(files).toHaveLength(2); - expect(files.map((f) => f.file_id)).toContain(ownedFileId); - expect(files.map((f) => f.file_id)).toContain(sharedFileId); - expect(files.map((f) => f.file_id)).not.toContain(inaccessibleFileId); - }); - - test('should return all files when no userId/agentId provided', async () => { - const userId = new mongoose.Types.ObjectId(); - const fileId1 = `file_${uuidv4()}`; - const fileId2 = `file_${uuidv4()}`; - - await createFile({ - file_id: fileId1, - user: userId, - filename: 'file1.txt', - filepath: '/uploads/file1.txt', - type: 'text/plain', - bytes: 100, - }); - - await createFile({ - file_id: fileId2, - user: new mongoose.Types.ObjectId(), - filename: 'file2.txt', - filepath: '/uploads/file2.txt', - type: 'text/plain', - bytes: 200, - }); - - const files = await getFiles({ file_id: { $in: [fileId1, fileId2] } }); - expect(files).toHaveLength(2); - }); - }); - - describe('Role-based file permissions', () => { - it('should optimize permission checks when role is provided', async () => { - const userId = new mongoose.Types.ObjectId(); - const authorId = new mongoose.Types.ObjectId(); - const agentId = uuidv4(); - const fileIds = [uuidv4(), uuidv4()]; - - // Create users - await User.create({ - _id: userId, - email: 'user@example.com', - emailVerified: true, - provider: 'local', - role: 'ADMIN', // User has ADMIN role - }); - - await User.create({ - _id: authorId, - email: 'author@example.com', - emailVerified: true, - provider: 'local', - }); - - // Create files - for (const fileId of fileIds) { - await createFile({ - file_id: fileId, - user: authorId, - filename: `${fileId}.txt`, - filepath: `/uploads/${fileId}.txt`, - type: 'text/plain', - bytes: 100, - }); - } - - // Create agent with files - const agent = await createAgent({ - id: agentId, - name: 'Test Agent', - author: authorId, - model: 'gpt-4', - provider: 'openai', - tool_resources: { - file_search: { - file_ids: fileIds, - }, - }, - }); - - // Grant permission to ADMIN role - await grantPermission({ - principalType: PrincipalType.ROLE, - principalId: 'ADMIN', - resourceType: ResourceType.AGENT, - resourceId: agent._id, - accessRoleId: AccessRoleIds.AGENT_EDITOR, - grantedBy: authorId, - }); - - // Check access with role provided (should avoid DB query) - const { hasAccessToFilesViaAgent } = require('~/server/services/Files/permissions'); - const accessMapWithRole = await hasAccessToFilesViaAgent({ - userId: userId, - role: 'ADMIN', - fileIds, - agentId: agent.id, - }); - - // User should have access through their ADMIN role - expect(accessMapWithRole.get(fileIds[0])).toBe(true); - expect(accessMapWithRole.get(fileIds[1])).toBe(true); - - // Check access without role (will query DB to get user's role) - const accessMapWithoutRole = await hasAccessToFilesViaAgent({ - userId: userId, - fileIds, - agentId: agent.id, - }); - - // Should have same result - expect(accessMapWithoutRole.get(fileIds[0])).toBe(true); - expect(accessMapWithoutRole.get(fileIds[1])).toBe(true); - }); - - it('should deny access when user role changes', async () => { - const userId = new mongoose.Types.ObjectId(); - const authorId = new mongoose.Types.ObjectId(); - const agentId = uuidv4(); - const fileId = uuidv4(); - - // Create users - await User.create({ - _id: userId, - email: 'user@example.com', - emailVerified: true, - provider: 'local', - role: 'EDITOR', - }); - - await User.create({ - _id: authorId, - email: 'author@example.com', - emailVerified: true, - provider: 'local', - }); - - // Create file - await createFile({ - file_id: fileId, - user: authorId, - filename: 'test.txt', - filepath: '/uploads/test.txt', - type: 'text/plain', - bytes: 100, - }); - - // Create agent - const agent = await createAgent({ - id: agentId, - name: 'Test Agent', - author: authorId, - model: 'gpt-4', - provider: 'openai', - tool_resources: { - file_search: { - file_ids: [fileId], - }, - }, - }); - - // Grant permission to EDITOR role only - await grantPermission({ - principalType: PrincipalType.ROLE, - principalId: 'EDITOR', - resourceType: ResourceType.AGENT, - resourceId: agent._id, - accessRoleId: AccessRoleIds.AGENT_EDITOR, - grantedBy: authorId, - }); - - const { hasAccessToFilesViaAgent } = require('~/server/services/Files/permissions'); - - // Check with EDITOR role - should have access - const accessAsEditor = await hasAccessToFilesViaAgent({ - userId: userId, - role: 'EDITOR', - fileIds: [fileId], - agentId: agent.id, - }); - expect(accessAsEditor.get(fileId)).toBe(true); - - // Simulate role change to USER - should lose access - const accessAsUser = await hasAccessToFilesViaAgent({ - userId: userId, - role: SystemRoles.USER, - fileIds: [fileId], - agentId: agent.id, - }); - expect(accessAsUser.get(fileId)).toBe(false); - }); - }); -}); diff --git a/api/models/Message.js b/api/models/Message.js deleted file mode 100644 index 8fe04f6f54..0000000000 --- a/api/models/Message.js +++ /dev/null @@ -1,372 +0,0 @@ -const { z } = require('zod'); -const { logger } = require('@librechat/data-schemas'); -const { createTempChatExpirationDate } = require('@librechat/api'); -const { Message } = require('~/db/models'); - -const idSchema = z.string().uuid(); - -/** - * Saves a message in the database. - * - * @async - * @function saveMessage - * @param {ServerRequest} req - The request object containing user information. - * @param {Object} params - The message data object. - * @param {string} params.endpoint - The endpoint where the message originated. - * @param {string} params.iconURL - The URL of the sender's icon. - * @param {string} params.messageId - The unique identifier for the message. - * @param {string} params.newMessageId - The new unique identifier for the message (if applicable). - * @param {string} params.conversationId - The identifier of the conversation. - * @param {string} [params.parentMessageId] - The identifier of the parent message, if any. - * @param {string} params.sender - The identifier of the sender. - * @param {string} params.text - The text content of the message. - * @param {boolean} params.isCreatedByUser - Indicates if the message was created by the user. - * @param {string} [params.error] - Any error associated with the message. - * @param {boolean} [params.unfinished] - Indicates if the message is unfinished. - * @param {Object[]} [params.files] - An array of files associated with the message. - * @param {string} [params.finish_reason] - Reason for finishing the message. - * @param {number} [params.tokenCount] - The number of tokens in the message. - * @param {string} [params.plugin] - Plugin associated with the message. - * @param {string[]} [params.plugins] - An array of plugins associated with the message. - * @param {string} [params.model] - The model used to generate the message. - * @param {Object} [metadata] - Additional metadata for this operation - * @param {string} [metadata.context] - The context of the operation - * @returns {Promise} The updated or newly inserted message document. - * @throws {Error} If there is an error in saving the message. - */ -async function saveMessage(req, params, metadata) { - if (!req?.user?.id) { - throw new Error('User not authenticated'); - } - - const validConvoId = idSchema.safeParse(params.conversationId); - if (!validConvoId.success) { - logger.warn(`Invalid conversation ID: ${params.conversationId}`); - logger.info(`---\`saveMessage\` context: ${metadata?.context}`); - logger.info(`---Invalid conversation ID Params: ${JSON.stringify(params, null, 2)}`); - return; - } - - try { - const update = { - ...params, - user: req.user.id, - messageId: params.newMessageId || params.messageId, - }; - - if (req?.body?.isTemporary) { - try { - const appConfig = req.config; - update.expiredAt = createTempChatExpirationDate(appConfig?.interfaceConfig); - } catch (err) { - logger.error('Error creating temporary chat expiration date:', err); - logger.info(`---\`saveMessage\` context: ${metadata?.context}`); - update.expiredAt = null; - } - } else { - update.expiredAt = null; - } - - if (update.tokenCount != null && isNaN(update.tokenCount)) { - logger.warn( - `Resetting invalid \`tokenCount\` for message \`${params.messageId}\`: ${update.tokenCount}`, - ); - logger.info(`---\`saveMessage\` context: ${metadata?.context}`); - update.tokenCount = 0; - } - const message = await Message.findOneAndUpdate( - { messageId: params.messageId, user: req.user.id }, - update, - { upsert: true, new: true }, - ); - - return message.toObject(); - } catch (err) { - logger.error('Error saving message:', err); - logger.info(`---\`saveMessage\` context: ${metadata?.context}`); - - // Check if this is a duplicate key error (MongoDB error code 11000) - if (err.code === 11000 && err.message.includes('duplicate key error')) { - // Log the duplicate key error but don't crash the application - logger.warn(`Duplicate messageId detected: ${params.messageId}. Continuing execution.`); - - try { - // Try to find the existing message with this ID - const existingMessage = await Message.findOne({ - messageId: params.messageId, - user: req.user.id, - }); - - // If we found it, return it - if (existingMessage) { - return existingMessage.toObject(); - } - - // If we can't find it (unlikely but possible in race conditions) - return { - ...params, - messageId: params.messageId, - user: req.user.id, - }; - } catch (findError) { - // If the findOne also fails, log it but don't crash - logger.warn( - `Could not retrieve existing message with ID ${params.messageId}: ${findError.message}`, - ); - return { - ...params, - messageId: params.messageId, - user: req.user.id, - }; - } - } - - throw err; // Re-throw other errors - } -} - -/** - * Saves multiple messages in the database in bulk. - * - * @async - * @function bulkSaveMessages - * @param {Object[]} messages - An array of message objects to save. - * @param {boolean} [overrideTimestamp=false] - Indicates whether to override the timestamps of the messages. Defaults to false. - * @returns {Promise} The result of the bulk write operation. - * @throws {Error} If there is an error in saving messages in bulk. - */ -async function bulkSaveMessages(messages, overrideTimestamp = false) { - try { - const bulkOps = messages.map((message) => ({ - updateOne: { - filter: { messageId: message.messageId }, - update: message, - timestamps: !overrideTimestamp, - upsert: true, - }, - })); - const result = await Message.bulkWrite(bulkOps); - return result; - } catch (err) { - logger.error('Error saving messages in bulk:', err); - throw err; - } -} - -/** - * Records a message in the database. - * - * @async - * @function recordMessage - * @param {Object} params - The message data object. - * @param {string} params.user - The identifier of the user. - * @param {string} params.endpoint - The endpoint where the message originated. - * @param {string} params.messageId - The unique identifier for the message. - * @param {string} params.conversationId - The identifier of the conversation. - * @param {string} [params.parentMessageId] - The identifier of the parent message, if any. - * @param {Partial} rest - Any additional properties from the TMessage typedef not explicitly listed. - * @returns {Promise} The updated or newly inserted message document. - * @throws {Error} If there is an error in saving the message. - */ -async function recordMessage({ - user, - endpoint, - messageId, - conversationId, - parentMessageId, - ...rest -}) { - try { - // No parsing of convoId as may use threadId - const message = { - user, - endpoint, - messageId, - conversationId, - parentMessageId, - ...rest, - }; - - return await Message.findOneAndUpdate({ user, messageId }, message, { - upsert: true, - new: true, - }); - } catch (err) { - logger.error('Error recording message:', err); - throw err; - } -} - -/** - * Updates the text of a message. - * - * @async - * @function updateMessageText - * @param {Object} params - The update data object. - * @param {Object} req - The request object. - * @param {string} params.messageId - The unique identifier for the message. - * @param {string} params.text - The new text content of the message. - * @returns {Promise} - * @throws {Error} If there is an error in updating the message text. - */ -async function updateMessageText(req, { messageId, text }) { - try { - await Message.updateOne({ messageId, user: req.user.id }, { text }); - } catch (err) { - logger.error('Error updating message text:', err); - throw err; - } -} - -/** - * Updates a message. - * - * @async - * @function updateMessage - * @param {Object} req - The request object. - * @param {Object} message - The message object containing update data. - * @param {string} message.messageId - The unique identifier for the message. - * @param {string} [message.text] - The new text content of the message. - * @param {Object[]} [message.files] - The files associated with the message. - * @param {boolean} [message.isCreatedByUser] - Indicates if the message was created by the user. - * @param {string} [message.sender] - The identifier of the sender. - * @param {number} [message.tokenCount] - The number of tokens in the message. - * @param {Object} [metadata] - The operation metadata - * @param {string} [metadata.context] - The operation metadata - * @returns {Promise} The updated message document. - * @throws {Error} If there is an error in updating the message or if the message is not found. - */ -async function updateMessage(req, message, metadata) { - try { - const { messageId, ...update } = message; - const updatedMessage = await Message.findOneAndUpdate( - { messageId, user: req.user.id }, - update, - { - new: true, - }, - ); - - if (!updatedMessage) { - throw new Error('Message not found or user not authorized.'); - } - - return { - messageId: updatedMessage.messageId, - conversationId: updatedMessage.conversationId, - parentMessageId: updatedMessage.parentMessageId, - sender: updatedMessage.sender, - text: updatedMessage.text, - isCreatedByUser: updatedMessage.isCreatedByUser, - tokenCount: updatedMessage.tokenCount, - feedback: updatedMessage.feedback, - }; - } catch (err) { - logger.error('Error updating message:', err); - if (metadata && metadata?.context) { - logger.info(`---\`updateMessage\` context: ${metadata.context}`); - } - throw err; - } -} - -/** - * Deletes messages in a conversation since a specific message. - * - * @async - * @function deleteMessagesSince - * @param {Object} params - The parameters object. - * @param {Object} req - The request object. - * @param {string} params.messageId - The unique identifier for the message. - * @param {string} params.conversationId - The identifier of the conversation. - * @returns {Promise} The number of deleted messages. - * @throws {Error} If there is an error in deleting messages. - */ -async function deleteMessagesSince(req, { messageId, conversationId }) { - try { - const message = await Message.findOne({ messageId, user: req.user.id }).lean(); - - if (message) { - const query = Message.find({ conversationId, user: req.user.id }); - return await query.deleteMany({ - createdAt: { $gt: message.createdAt }, - }); - } - return undefined; - } catch (err) { - logger.error('Error deleting messages:', err); - throw err; - } -} - -/** - * Retrieves messages from the database. - * @async - * @function getMessages - * @param {Record} filter - The filter criteria. - * @param {string | undefined} [select] - The fields to select. - * @returns {Promise} The messages that match the filter criteria. - * @throws {Error} If there is an error in retrieving messages. - */ -async function getMessages(filter, select) { - try { - if (select) { - return await Message.find(filter).select(select).sort({ createdAt: 1 }).lean(); - } - - return await Message.find(filter).sort({ createdAt: 1 }).lean(); - } catch (err) { - logger.error('Error getting messages:', err); - throw err; - } -} - -/** - * Retrieves a single message from the database. - * @async - * @function getMessage - * @param {{ user: string, messageId: string }} params - The search parameters - * @returns {Promise} The message that matches the criteria or null if not found - * @throws {Error} If there is an error in retrieving the message - */ -async function getMessage({ user, messageId }) { - try { - return await Message.findOne({ - user, - messageId, - }).lean(); - } catch (err) { - logger.error('Error getting message:', err); - throw err; - } -} - -/** - * Deletes messages from the database. - * - * @async - * @function deleteMessages - * @param {import('mongoose').FilterQuery} filter - The filter criteria to find messages to delete. - * @returns {Promise} The metadata with count of deleted messages. - * @throws {Error} If there is an error in deleting messages. - */ -async function deleteMessages(filter) { - try { - return await Message.deleteMany(filter); - } catch (err) { - logger.error('Error deleting messages:', err); - throw err; - } -} - -module.exports = { - saveMessage, - bulkSaveMessages, - recordMessage, - updateMessageText, - updateMessage, - deleteMessagesSince, - getMessages, - getMessage, - deleteMessages, -}; diff --git a/api/models/Preset.js b/api/models/Preset.js deleted file mode 100644 index 4db3d59066..0000000000 --- a/api/models/Preset.js +++ /dev/null @@ -1,82 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); -const { Preset } = require('~/db/models'); - -const getPreset = async (user, presetId) => { - try { - return await Preset.findOne({ user, presetId }).lean(); - } catch (error) { - logger.error('[getPreset] Error getting single preset', error); - return { message: 'Error getting single preset' }; - } -}; - -module.exports = { - getPreset, - getPresets: async (user, filter) => { - try { - const presets = await Preset.find({ ...filter, user }).lean(); - const defaultValue = 10000; - - presets.sort((a, b) => { - let orderA = a.order !== undefined ? a.order : defaultValue; - let orderB = b.order !== undefined ? b.order : defaultValue; - - if (orderA !== orderB) { - return orderA - orderB; - } - - return b.updatedAt - a.updatedAt; - }); - - return presets; - } catch (error) { - logger.error('[getPresets] Error getting presets', error); - return { message: 'Error retrieving presets' }; - } - }, - savePreset: async (user, { presetId, newPresetId, defaultPreset, ...preset }) => { - try { - const setter = { $set: {} }; - const { user: _, ...cleanPreset } = preset; - const update = { presetId, ...cleanPreset }; - if (preset.tools && Array.isArray(preset.tools)) { - update.tools = - preset.tools - .map((tool) => tool?.pluginKey ?? tool) - .filter((toolName) => typeof toolName === 'string') ?? []; - } - if (newPresetId) { - update.presetId = newPresetId; - } - - if (defaultPreset) { - update.defaultPreset = defaultPreset; - update.order = 0; - - const currentDefault = await Preset.findOne({ defaultPreset: true, user }); - - if (currentDefault && currentDefault.presetId !== presetId) { - await Preset.findByIdAndUpdate(currentDefault._id, { - $unset: { defaultPreset: '', order: '' }, - }); - } - } else if (defaultPreset === false) { - update.defaultPreset = undefined; - update.order = undefined; - setter['$unset'] = { defaultPreset: '', order: '' }; - } - - setter.$set = update; - return await Preset.findOneAndUpdate({ presetId, user }, setter, { new: true, upsert: true }); - } catch (error) { - logger.error('[savePreset] Error saving preset', error); - return { message: 'Error saving preset' }; - } - }, - deletePresets: async (user, filter) => { - // let toRemove = await Preset.find({ ...filter, user }).select('presetId'); - // const ids = toRemove.map((instance) => instance.presetId); - let deleteCount = await Preset.deleteMany({ ...filter, user }); - return deleteCount; - }, -}; diff --git a/api/models/Project.js b/api/models/Project.js deleted file mode 100644 index 8fd1e556f9..0000000000 --- a/api/models/Project.js +++ /dev/null @@ -1,133 +0,0 @@ -const { GLOBAL_PROJECT_NAME } = require('librechat-data-provider').Constants; -const { Project } = require('~/db/models'); - -/** - * Retrieve a project by ID and convert the found project document to a plain object. - * - * @param {string} projectId - The ID of the project to find and return as a plain object. - * @param {string|string[]} [fieldsToSelect] - The fields to include or exclude in the returned document. - * @returns {Promise} A plain object representing the project document, or `null` if no project is found. - */ -const getProjectById = async function (projectId, fieldsToSelect = null) { - const query = Project.findById(projectId); - - if (fieldsToSelect) { - query.select(fieldsToSelect); - } - - return await query.lean(); -}; - -/** - * Retrieve a project by name and convert the found project document to a plain object. - * If the project with the given name doesn't exist and the name is "instance", create it and return the lean version. - * - * @param {string} projectName - The name of the project to find or create. - * @param {string|string[]} [fieldsToSelect] - The fields to include or exclude in the returned document. - * @returns {Promise} A plain object representing the project document. - */ -const getProjectByName = async function (projectName, fieldsToSelect = null) { - const query = { name: projectName }; - const update = { $setOnInsert: { name: projectName } }; - const options = { - new: true, - upsert: projectName === GLOBAL_PROJECT_NAME, - lean: true, - select: fieldsToSelect, - }; - - return await Project.findOneAndUpdate(query, update, options); -}; - -/** - * Add an array of prompt group IDs to a project's promptGroupIds array, ensuring uniqueness. - * - * @param {string} projectId - The ID of the project to update. - * @param {string[]} promptGroupIds - The array of prompt group IDs to add to the project. - * @returns {Promise} The updated project document. - */ -const addGroupIdsToProject = async function (projectId, promptGroupIds) { - return await Project.findByIdAndUpdate( - projectId, - { $addToSet: { promptGroupIds: { $each: promptGroupIds } } }, - { new: true }, - ); -}; - -/** - * Remove an array of prompt group IDs from a project's promptGroupIds array. - * - * @param {string} projectId - The ID of the project to update. - * @param {string[]} promptGroupIds - The array of prompt group IDs to remove from the project. - * @returns {Promise} The updated project document. - */ -const removeGroupIdsFromProject = async function (projectId, promptGroupIds) { - return await Project.findByIdAndUpdate( - projectId, - { $pull: { promptGroupIds: { $in: promptGroupIds } } }, - { new: true }, - ); -}; - -/** - * Remove a prompt group ID from all projects. - * - * @param {string} promptGroupId - The ID of the prompt group to remove from projects. - * @returns {Promise} - */ -const removeGroupFromAllProjects = async (promptGroupId) => { - await Project.updateMany({}, { $pull: { promptGroupIds: promptGroupId } }); -}; - -/** - * Add an array of agent IDs to a project's agentIds array, ensuring uniqueness. - * - * @param {string} projectId - The ID of the project to update. - * @param {string[]} agentIds - The array of agent IDs to add to the project. - * @returns {Promise} The updated project document. - */ -const addAgentIdsToProject = async function (projectId, agentIds) { - return await Project.findByIdAndUpdate( - projectId, - { $addToSet: { agentIds: { $each: agentIds } } }, - { new: true }, - ); -}; - -/** - * Remove an array of agent IDs from a project's agentIds array. - * - * @param {string} projectId - The ID of the project to update. - * @param {string[]} agentIds - The array of agent IDs to remove from the project. - * @returns {Promise} The updated project document. - */ -const removeAgentIdsFromProject = async function (projectId, agentIds) { - return await Project.findByIdAndUpdate( - projectId, - { $pull: { agentIds: { $in: agentIds } } }, - { new: true }, - ); -}; - -/** - * Remove an agent ID from all projects. - * - * @param {string} agentId - The ID of the agent to remove from projects. - * @returns {Promise} - */ -const removeAgentFromAllProjects = async (agentId) => { - await Project.updateMany({}, { $pull: { agentIds: agentId } }); -}; - -module.exports = { - getProjectById, - getProjectByName, - /* prompts */ - addGroupIdsToProject, - removeGroupIdsFromProject, - removeGroupFromAllProjects, - /* agents */ - addAgentIdsToProject, - removeAgentIdsFromProject, - removeAgentFromAllProjects, -}; diff --git a/api/models/Prompt.js b/api/models/Prompt.js deleted file mode 100644 index bde911b23a..0000000000 --- a/api/models/Prompt.js +++ /dev/null @@ -1,708 +0,0 @@ -const { ObjectId } = require('mongodb'); -const { escapeRegExp } = require('@librechat/api'); -const { logger } = require('@librechat/data-schemas'); -const { - Constants, - SystemRoles, - ResourceType, - SystemCategories, -} = require('librechat-data-provider'); -const { - removeGroupFromAllProjects, - removeGroupIdsFromProject, - addGroupIdsToProject, - getProjectByName, -} = require('./Project'); -const { removeAllPermissions } = require('~/server/services/PermissionService'); -const { PromptGroup, Prompt, AclEntry } = require('~/db/models'); - -/** - * Create a pipeline for the aggregation to get prompt groups - * @param {Object} query - * @param {number} skip - * @param {number} limit - * @returns {[Object]} - The pipeline for the aggregation - */ -const createGroupPipeline = (query, skip, limit) => { - return [ - { $match: query }, - { $sort: { createdAt: -1 } }, - { $skip: skip }, - { $limit: limit }, - { - $lookup: { - from: 'prompts', - localField: 'productionId', - foreignField: '_id', - as: 'productionPrompt', - }, - }, - { $unwind: { path: '$productionPrompt', preserveNullAndEmptyArrays: true } }, - { - $project: { - name: 1, - numberOfGenerations: 1, - oneliner: 1, - category: 1, - projectIds: 1, - productionId: 1, - author: 1, - authorName: 1, - createdAt: 1, - updatedAt: 1, - 'productionPrompt.prompt': 1, - // 'productionPrompt._id': 1, - // 'productionPrompt.type': 1, - }, - }, - ]; -}; - -/** - * Create a pipeline for the aggregation to get all prompt groups - * @param {Object} query - * @param {Partial} $project - * @returns {[Object]} - The pipeline for the aggregation - */ -const createAllGroupsPipeline = ( - query, - $project = { - name: 1, - oneliner: 1, - category: 1, - author: 1, - authorName: 1, - createdAt: 1, - updatedAt: 1, - command: 1, - 'productionPrompt.prompt': 1, - }, -) => { - return [ - { $match: query }, - { $sort: { createdAt: -1 } }, - { - $lookup: { - from: 'prompts', - localField: 'productionId', - foreignField: '_id', - as: 'productionPrompt', - }, - }, - { $unwind: { path: '$productionPrompt', preserveNullAndEmptyArrays: true } }, - { - $project, - }, - ]; -}; - -/** - * Get all prompt groups with filters - * @param {ServerRequest} req - * @param {TPromptGroupsWithFilterRequest} filter - * @returns {Promise} - */ -const getAllPromptGroups = async (req, filter) => { - try { - const { name, ...query } = filter; - - let searchShared = true; - let searchSharedOnly = false; - if (name) { - query.name = new RegExp(escapeRegExp(name), 'i'); - } - if (!query.category) { - delete query.category; - } else if (query.category === SystemCategories.MY_PROMPTS) { - searchShared = false; - delete query.category; - } else if (query.category === SystemCategories.NO_CATEGORY) { - query.category = ''; - } else if (query.category === SystemCategories.SHARED_PROMPTS) { - searchSharedOnly = true; - delete query.category; - } - - let combinedQuery = query; - - if (searchShared) { - const project = await getProjectByName(Constants.GLOBAL_PROJECT_NAME, 'promptGroupIds'); - if (project && project.promptGroupIds && project.promptGroupIds.length > 0) { - const projectQuery = { _id: { $in: project.promptGroupIds }, ...query }; - delete projectQuery.author; - combinedQuery = searchSharedOnly ? projectQuery : { $or: [projectQuery, query] }; - } - } - - const promptGroupsPipeline = createAllGroupsPipeline(combinedQuery); - return await PromptGroup.aggregate(promptGroupsPipeline).exec(); - } catch (error) { - console.error('Error getting all prompt groups', error); - return { message: 'Error getting all prompt groups' }; - } -}; - -/** - * Get prompt groups with filters - * @param {ServerRequest} req - * @param {TPromptGroupsWithFilterRequest} filter - * @returns {Promise} - */ -const getPromptGroups = async (req, filter) => { - try { - const { pageNumber = 1, pageSize = 10, name, ...query } = filter; - - const validatedPageNumber = Math.max(parseInt(pageNumber, 10), 1); - const validatedPageSize = Math.max(parseInt(pageSize, 10), 1); - - let searchShared = true; - let searchSharedOnly = false; - if (name) { - query.name = new RegExp(escapeRegExp(name), 'i'); - } - if (!query.category) { - delete query.category; - } else if (query.category === SystemCategories.MY_PROMPTS) { - searchShared = false; - delete query.category; - } else if (query.category === SystemCategories.NO_CATEGORY) { - query.category = ''; - } else if (query.category === SystemCategories.SHARED_PROMPTS) { - searchSharedOnly = true; - delete query.category; - } - - let combinedQuery = query; - - if (searchShared) { - // const projects = req.user.projects || []; // TODO: handle multiple projects - const project = await getProjectByName(Constants.GLOBAL_PROJECT_NAME, 'promptGroupIds'); - if (project && project.promptGroupIds && project.promptGroupIds.length > 0) { - const projectQuery = { _id: { $in: project.promptGroupIds }, ...query }; - delete projectQuery.author; - combinedQuery = searchSharedOnly ? projectQuery : { $or: [projectQuery, query] }; - } - } - - const skip = (validatedPageNumber - 1) * validatedPageSize; - const limit = validatedPageSize; - - const promptGroupsPipeline = createGroupPipeline(combinedQuery, skip, limit); - const totalPromptGroupsPipeline = [{ $match: combinedQuery }, { $count: 'total' }]; - - const [promptGroupsResults, totalPromptGroupsResults] = await Promise.all([ - PromptGroup.aggregate(promptGroupsPipeline).exec(), - PromptGroup.aggregate(totalPromptGroupsPipeline).exec(), - ]); - - const promptGroups = promptGroupsResults; - const totalPromptGroups = - totalPromptGroupsResults.length > 0 ? totalPromptGroupsResults[0].total : 0; - - return { - promptGroups, - pageNumber: validatedPageNumber.toString(), - pageSize: validatedPageSize.toString(), - pages: Math.ceil(totalPromptGroups / validatedPageSize).toString(), - }; - } catch (error) { - console.error('Error getting prompt groups', error); - return { message: 'Error getting prompt groups' }; - } -}; - -/** - * @param {Object} fields - * @param {string} fields._id - * @param {string} fields.author - * @param {string} fields.role - * @returns {Promise} - */ -const deletePromptGroup = async ({ _id, author, role }) => { - // Build query - with ACL, author is optional - const query = { _id }; - const groupQuery = { groupId: new ObjectId(_id) }; - - // Legacy: Add author filter if provided (backward compatibility) - if (author && role !== SystemRoles.ADMIN) { - query.author = author; - groupQuery.author = author; - } - - const response = await PromptGroup.deleteOne(query); - - if (!response || response.deletedCount === 0) { - throw new Error('Prompt group not found'); - } - - await Prompt.deleteMany(groupQuery); - await removeGroupFromAllProjects(_id); - - try { - await removeAllPermissions({ resourceType: ResourceType.PROMPTGROUP, resourceId: _id }); - } catch (error) { - logger.error('Error removing promptGroup permissions:', error); - } - - return { message: 'Prompt group deleted successfully' }; -}; - -/** - * Get prompt groups by accessible IDs with optional cursor-based pagination. - * @param {Object} params - The parameters for getting accessible prompt groups. - * @param {Array} [params.accessibleIds] - Array of prompt group ObjectIds the user has ACL access to. - * @param {Object} [params.otherParams] - Additional query parameters (including author filter). - * @param {number} [params.limit] - Number of prompt groups to return (max 100). If not provided, returns all prompt groups. - * @param {string} [params.after] - Cursor for pagination - get prompt groups after this cursor. // base64 encoded JSON string with updatedAt and _id. - * @returns {Promise} A promise that resolves to an object containing the prompt groups data and pagination info. - */ -async function getListPromptGroupsByAccess({ - accessibleIds = [], - otherParams = {}, - limit = null, - after = null, -}) { - const isPaginated = limit !== null && limit !== undefined; - const normalizedLimit = isPaginated ? Math.min(Math.max(1, parseInt(limit) || 20), 100) : null; - - // Build base query combining ACL accessible prompt groups with other filters - const baseQuery = { ...otherParams, _id: { $in: accessibleIds } }; - - // Add cursor condition - if (after && typeof after === 'string' && after !== 'undefined' && after !== 'null') { - try { - const cursor = JSON.parse(Buffer.from(after, 'base64').toString('utf8')); - const { updatedAt, _id } = cursor; - - const cursorCondition = { - $or: [ - { updatedAt: { $lt: new Date(updatedAt) } }, - { updatedAt: new Date(updatedAt), _id: { $gt: new ObjectId(_id) } }, - ], - }; - - // Merge cursor condition with base query - if (Object.keys(baseQuery).length > 0) { - baseQuery.$and = [{ ...baseQuery }, cursorCondition]; - // Remove the original conditions from baseQuery to avoid duplication - Object.keys(baseQuery).forEach((key) => { - if (key !== '$and') delete baseQuery[key]; - }); - } else { - Object.assign(baseQuery, cursorCondition); - } - } catch (error) { - logger.warn('Invalid cursor:', error.message); - } - } - - // Build aggregation pipeline - const pipeline = [{ $match: baseQuery }, { $sort: { updatedAt: -1, _id: 1 } }]; - - // Only apply limit if pagination is requested - if (isPaginated) { - pipeline.push({ $limit: normalizedLimit + 1 }); - } - - // Add lookup for production prompt - pipeline.push( - { - $lookup: { - from: 'prompts', - localField: 'productionId', - foreignField: '_id', - as: 'productionPrompt', - }, - }, - { $unwind: { path: '$productionPrompt', preserveNullAndEmptyArrays: true } }, - { - $project: { - name: 1, - numberOfGenerations: 1, - oneliner: 1, - category: 1, - projectIds: 1, - productionId: 1, - author: 1, - authorName: 1, - createdAt: 1, - updatedAt: 1, - 'productionPrompt.prompt': 1, - }, - }, - ); - - const promptGroups = await PromptGroup.aggregate(pipeline).exec(); - - const hasMore = isPaginated ? promptGroups.length > normalizedLimit : false; - const data = (isPaginated ? promptGroups.slice(0, normalizedLimit) : promptGroups).map( - (group) => { - if (group.author) { - group.author = group.author.toString(); - } - return group; - }, - ); - - // Generate next cursor only if paginated - let nextCursor = null; - if (isPaginated && hasMore && data.length > 0) { - const lastGroup = promptGroups[normalizedLimit - 1]; - nextCursor = Buffer.from( - JSON.stringify({ - updatedAt: lastGroup.updatedAt.toISOString(), - _id: lastGroup._id.toString(), - }), - ).toString('base64'); - } - - return { - object: 'list', - data, - first_id: data.length > 0 ? data[0]._id.toString() : null, - last_id: data.length > 0 ? data[data.length - 1]._id.toString() : null, - has_more: hasMore, - after: nextCursor, - }; -} - -module.exports = { - getPromptGroups, - deletePromptGroup, - getAllPromptGroups, - getListPromptGroupsByAccess, - /** - * Create a prompt and its respective group - * @param {TCreatePromptRecord} saveData - * @returns {Promise} - */ - createPromptGroup: async (saveData) => { - try { - const { prompt, group, author, authorName } = saveData; - - let newPromptGroup = await PromptGroup.findOneAndUpdate( - { ...group, author, authorName, productionId: null }, - { $setOnInsert: { ...group, author, authorName, productionId: null } }, - { new: true, upsert: true }, - ) - .lean() - .select('-__v') - .exec(); - - const newPrompt = await Prompt.findOneAndUpdate( - { ...prompt, author, groupId: newPromptGroup._id }, - { $setOnInsert: { ...prompt, author, groupId: newPromptGroup._id } }, - { new: true, upsert: true }, - ) - .lean() - .select('-__v') - .exec(); - - newPromptGroup = await PromptGroup.findByIdAndUpdate( - newPromptGroup._id, - { productionId: newPrompt._id }, - { new: true }, - ) - .lean() - .select('-__v') - .exec(); - - return { - prompt: newPrompt, - group: { - ...newPromptGroup, - productionPrompt: { prompt: newPrompt.prompt }, - }, - }; - } catch (error) { - logger.error('Error saving prompt group', error); - throw new Error('Error saving prompt group'); - } - }, - /** - * Save a prompt - * @param {TCreatePromptRecord} saveData - * @returns {Promise} - */ - savePrompt: async (saveData) => { - try { - const { prompt, author } = saveData; - const newPromptData = { - ...prompt, - author, - }; - - /** @type {TPrompt} */ - let newPrompt; - try { - newPrompt = await Prompt.create(newPromptData); - } catch (error) { - if (error?.message?.includes('groupId_1_version_1')) { - await Prompt.db.collection('prompts').dropIndex('groupId_1_version_1'); - } else { - throw error; - } - newPrompt = await Prompt.create(newPromptData); - } - - return { prompt: newPrompt }; - } catch (error) { - logger.error('Error saving prompt', error); - return { message: 'Error saving prompt' }; - } - }, - getPrompts: async (filter) => { - try { - return await Prompt.find(filter).sort({ createdAt: -1 }).lean(); - } catch (error) { - logger.error('Error getting prompts', error); - return { message: 'Error getting prompts' }; - } - }, - getPrompt: async (filter) => { - try { - if (filter.groupId) { - filter.groupId = new ObjectId(filter.groupId); - } - return await Prompt.findOne(filter).lean(); - } catch (error) { - logger.error('Error getting prompt', error); - return { message: 'Error getting prompt' }; - } - }, - /** - * Get prompt groups with filters - * @param {TGetRandomPromptsRequest} filter - * @returns {Promise} - */ - getRandomPromptGroups: async (filter) => { - try { - const result = await PromptGroup.aggregate([ - { - $match: { - category: { $ne: '' }, - }, - }, - { - $group: { - _id: '$category', - promptGroup: { $first: '$$ROOT' }, - }, - }, - { - $replaceRoot: { newRoot: '$promptGroup' }, - }, - { - $sample: { size: +filter.limit + +filter.skip }, - }, - { - $skip: +filter.skip, - }, - { - $limit: +filter.limit, - }, - ]); - return { prompts: result }; - } catch (error) { - logger.error('Error getting prompt groups', error); - return { message: 'Error getting prompt groups' }; - } - }, - getPromptGroupsWithPrompts: async (filter) => { - try { - return await PromptGroup.findOne(filter) - .populate({ - path: 'prompts', - select: '-_id -__v -user', - }) - .select('-_id -__v -user') - .lean(); - } catch (error) { - logger.error('Error getting prompt groups', error); - return { message: 'Error getting prompt groups' }; - } - }, - getPromptGroup: async (filter) => { - try { - return await PromptGroup.findOne(filter).lean(); - } catch (error) { - logger.error('Error getting prompt group', error); - return { message: 'Error getting prompt group' }; - } - }, - /** - * Deletes a prompt and its corresponding prompt group if it is the last prompt in the group. - * - * @param {Object} options - The options for deleting the prompt. - * @param {ObjectId|string} options.promptId - The ID of the prompt to delete. - * @param {ObjectId|string} options.groupId - The ID of the prompt's group. - * @param {ObjectId|string} options.author - The ID of the prompt's author. - * @param {string} options.role - The role of the prompt's author. - * @return {Promise} An object containing the result of the deletion. - * If the prompt was deleted successfully, the object will have a property 'prompt' with the value 'Prompt deleted successfully'. - * If the prompt group was deleted successfully, the object will have a property 'promptGroup' with the message 'Prompt group deleted successfully' and id of the deleted group. - * If there was an error deleting the prompt, the object will have a property 'message' with the value 'Error deleting prompt'. - */ - deletePrompt: async ({ promptId, groupId, author, role }) => { - const query = { _id: promptId, groupId, author }; - if (role === SystemRoles.ADMIN) { - delete query.author; - } - const { deletedCount } = await Prompt.deleteOne(query); - if (deletedCount === 0) { - throw new Error('Failed to delete the prompt'); - } - - const remainingPrompts = await Prompt.find({ groupId }) - .select('_id') - .sort({ createdAt: 1 }) - .lean(); - - if (remainingPrompts.length === 0) { - // Remove all ACL entries for the promptGroup when deleting the last prompt - try { - await removeAllPermissions({ - resourceType: ResourceType.PROMPTGROUP, - resourceId: groupId, - }); - } catch (error) { - logger.error('Error removing promptGroup permissions:', error); - } - - await PromptGroup.deleteOne({ _id: groupId }); - await removeGroupFromAllProjects(groupId); - - return { - prompt: 'Prompt deleted successfully', - promptGroup: { - message: 'Prompt group deleted successfully', - id: groupId, - }, - }; - } else { - const promptGroup = await PromptGroup.findById(groupId).lean(); - if (promptGroup.productionId.toString() === promptId.toString()) { - await PromptGroup.updateOne( - { _id: groupId }, - { productionId: remainingPrompts[remainingPrompts.length - 1]._id }, - ); - } - - return { prompt: 'Prompt deleted successfully' }; - } - }, - /** - * Delete all prompts and prompt groups created by a specific user. - * @param {ServerRequest} req - The server request object. - * @param {string} userId - The ID of the user whose prompts and prompt groups are to be deleted. - */ - deleteUserPrompts: async (req, userId) => { - try { - const promptGroups = await getAllPromptGroups(req, { author: new ObjectId(userId) }); - - if (promptGroups.length === 0) { - return; - } - - const groupIds = promptGroups.map((group) => group._id); - - for (const groupId of groupIds) { - await removeGroupFromAllProjects(groupId); - } - - await AclEntry.deleteMany({ - resourceType: ResourceType.PROMPTGROUP, - resourceId: { $in: groupIds }, - }); - - await PromptGroup.deleteMany({ author: new ObjectId(userId) }); - await Prompt.deleteMany({ author: new ObjectId(userId) }); - } catch (error) { - logger.error('[deleteUserPrompts] General error:', error); - } - }, - /** - * Update prompt group - * @param {Partial} filter - Filter to find prompt group - * @param {Partial} data - Data to update - * @returns {Promise} - */ - updatePromptGroup: async (filter, data) => { - try { - const updateOps = {}; - if (data.removeProjectIds) { - for (const projectId of data.removeProjectIds) { - await removeGroupIdsFromProject(projectId, [filter._id]); - } - - updateOps.$pull = { projectIds: { $in: data.removeProjectIds } }; - delete data.removeProjectIds; - } - - if (data.projectIds) { - for (const projectId of data.projectIds) { - await addGroupIdsToProject(projectId, [filter._id]); - } - - updateOps.$addToSet = { projectIds: { $each: data.projectIds } }; - delete data.projectIds; - } - - const updateData = { ...data, ...updateOps }; - const updatedDoc = await PromptGroup.findOneAndUpdate(filter, updateData, { - new: true, - upsert: false, - }); - - if (!updatedDoc) { - throw new Error('Prompt group not found'); - } - - return updatedDoc; - } catch (error) { - logger.error('Error updating prompt group', error); - return { message: 'Error updating prompt group' }; - } - }, - /** - * Function to make a prompt production based on its ID. - * @param {String} promptId - The ID of the prompt to make production. - * @returns {Object} The result of the production operation. - */ - makePromptProduction: async (promptId) => { - try { - const prompt = await Prompt.findById(promptId).lean(); - - if (!prompt) { - throw new Error('Prompt not found'); - } - - await PromptGroup.findByIdAndUpdate( - prompt.groupId, - { productionId: prompt._id }, - { new: true }, - ) - .lean() - .exec(); - - return { - message: 'Prompt production made successfully', - }; - } catch (error) { - logger.error('Error making prompt production', error); - return { message: 'Error making prompt production' }; - } - }, - updatePromptLabels: async (_id, labels) => { - try { - const response = await Prompt.updateOne({ _id }, { $set: { labels } }); - if (response.matchedCount === 0) { - return { message: 'Prompt not found' }; - } - return { message: 'Prompt labels updated successfully' }; - } catch (error) { - logger.error('Error updating prompt labels', error); - return { message: 'Error updating prompt labels' }; - } - }, -}; diff --git a/api/models/Prompt.spec.js b/api/models/Prompt.spec.js deleted file mode 100644 index e00a1a518c..0000000000 --- a/api/models/Prompt.spec.js +++ /dev/null @@ -1,564 +0,0 @@ -const mongoose = require('mongoose'); -const { ObjectId } = require('mongodb'); -const { logger } = require('@librechat/data-schemas'); -const { MongoMemoryServer } = require('mongodb-memory-server'); -const { - SystemRoles, - ResourceType, - AccessRoleIds, - PrincipalType, - PermissionBits, -} = require('librechat-data-provider'); - -// Mock the config/connect module to prevent connection attempts during tests -jest.mock('../../config/connect', () => jest.fn().mockResolvedValue(true)); - -const dbModels = require('~/db/models'); - -// Disable console for tests -logger.silent = true; - -let mongoServer; -let Prompt, PromptGroup, AclEntry, AccessRole, User, Group, Project; -let promptFns, permissionService; -let testUsers, testGroups, testRoles; - -beforeAll(async () => { - // Set up MongoDB memory server - mongoServer = await MongoMemoryServer.create(); - const mongoUri = mongoServer.getUri(); - await mongoose.connect(mongoUri); - - // Initialize models - Prompt = dbModels.Prompt; - PromptGroup = dbModels.PromptGroup; - AclEntry = dbModels.AclEntry; - AccessRole = dbModels.AccessRole; - User = dbModels.User; - Group = dbModels.Group; - Project = dbModels.Project; - - promptFns = require('~/models/Prompt'); - permissionService = require('~/server/services/PermissionService'); - - // Create test data - await setupTestData(); -}); - -afterAll(async () => { - await mongoose.disconnect(); - await mongoServer.stop(); - jest.clearAllMocks(); -}); - -async function setupTestData() { - // Create access roles for promptGroups - testRoles = { - viewer: await AccessRole.create({ - accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER, - name: 'Viewer', - description: 'Can view promptGroups', - resourceType: ResourceType.PROMPTGROUP, - permBits: PermissionBits.VIEW, - }), - editor: await AccessRole.create({ - accessRoleId: AccessRoleIds.PROMPTGROUP_EDITOR, - name: 'Editor', - description: 'Can view and edit promptGroups', - resourceType: ResourceType.PROMPTGROUP, - permBits: PermissionBits.VIEW | PermissionBits.EDIT, - }), - owner: await AccessRole.create({ - accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER, - name: 'Owner', - description: 'Full control over promptGroups', - resourceType: ResourceType.PROMPTGROUP, - permBits: - PermissionBits.VIEW | PermissionBits.EDIT | PermissionBits.DELETE | PermissionBits.SHARE, - }), - }; - - // Create test users - testUsers = { - owner: await User.create({ - name: 'Prompt Owner', - email: 'owner@example.com', - role: SystemRoles.USER, - }), - editor: await User.create({ - name: 'Prompt Editor', - email: 'editor@example.com', - role: SystemRoles.USER, - }), - viewer: await User.create({ - name: 'Prompt Viewer', - email: 'viewer@example.com', - role: SystemRoles.USER, - }), - admin: await User.create({ - name: 'Admin User', - email: 'admin@example.com', - role: SystemRoles.ADMIN, - }), - noAccess: await User.create({ - name: 'No Access User', - email: 'noaccess@example.com', - role: SystemRoles.USER, - }), - }; - - // Create test groups - testGroups = { - editors: await Group.create({ - name: 'Prompt Editors', - description: 'Group with editor access', - }), - viewers: await Group.create({ - name: 'Prompt Viewers', - description: 'Group with viewer access', - }), - }; - - await Project.create({ - name: 'Global', - description: 'Global project', - promptGroupIds: [], - }); -} - -describe('Prompt ACL Permissions', () => { - describe('Creating Prompts with Permissions', () => { - it('should grant owner permissions when creating a prompt', async () => { - // First create a group - const testGroup = await PromptGroup.create({ - name: 'Test Group', - category: 'testing', - author: testUsers.owner._id, - authorName: testUsers.owner.name, - productionId: new mongoose.Types.ObjectId(), - }); - - const promptData = { - prompt: { - prompt: 'Test prompt content', - name: 'Test Prompt', - type: 'text', - groupId: testGroup._id, - }, - author: testUsers.owner._id, - }; - - await promptFns.savePrompt(promptData); - - // Manually grant permissions as would happen in the route - await permissionService.grantPermission({ - principalType: PrincipalType.USER, - principalId: testUsers.owner._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testGroup._id, - accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER, - grantedBy: testUsers.owner._id, - }); - - // Check ACL entry - const aclEntry = await AclEntry.findOne({ - resourceType: ResourceType.PROMPTGROUP, - resourceId: testGroup._id, - principalType: PrincipalType.USER, - principalId: testUsers.owner._id, - }); - - expect(aclEntry).toBeTruthy(); - expect(aclEntry.permBits).toBe(testRoles.owner.permBits); - }); - }); - - describe('Accessing Prompts', () => { - let testPromptGroup; - - beforeEach(async () => { - // Create a prompt group - testPromptGroup = await PromptGroup.create({ - name: 'Test Group', - author: testUsers.owner._id, - authorName: testUsers.owner.name, - productionId: new ObjectId(), - }); - - // Create a prompt - await Prompt.create({ - prompt: 'Test prompt for access control', - name: 'Access Test Prompt', - author: testUsers.owner._id, - groupId: testPromptGroup._id, - type: 'text', - }); - - // Grant owner permissions - await permissionService.grantPermission({ - principalType: PrincipalType.USER, - principalId: testUsers.owner._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER, - grantedBy: testUsers.owner._id, - }); - }); - - afterEach(async () => { - await Prompt.deleteMany({}); - await PromptGroup.deleteMany({}); - await AclEntry.deleteMany({}); - }); - - it('owner should have full access to their prompt', async () => { - const hasAccess = await permissionService.checkPermission({ - userId: testUsers.owner._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - requiredPermission: PermissionBits.VIEW, - }); - - expect(hasAccess).toBe(true); - - const canEdit = await permissionService.checkPermission({ - userId: testUsers.owner._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - requiredPermission: PermissionBits.EDIT, - }); - - expect(canEdit).toBe(true); - }); - - it('user with viewer role should only have view access', async () => { - // Grant viewer permissions - await permissionService.grantPermission({ - principalType: PrincipalType.USER, - principalId: testUsers.viewer._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER, - grantedBy: testUsers.owner._id, - }); - - const canView = await permissionService.checkPermission({ - userId: testUsers.viewer._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - requiredPermission: PermissionBits.VIEW, - }); - - const canEdit = await permissionService.checkPermission({ - userId: testUsers.viewer._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - requiredPermission: PermissionBits.EDIT, - }); - - expect(canView).toBe(true); - expect(canEdit).toBe(false); - }); - - it('user without permissions should have no access', async () => { - const hasAccess = await permissionService.checkPermission({ - userId: testUsers.noAccess._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - requiredPermission: PermissionBits.VIEW, - }); - - expect(hasAccess).toBe(false); - }); - - it('admin should have access regardless of permissions', async () => { - // Admin users should work through normal permission system - // The middleware layer handles admin bypass, not the permission service - const hasAccess = await permissionService.checkPermission({ - userId: testUsers.admin._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - requiredPermission: PermissionBits.VIEW, - }); - - // Without explicit permissions, even admin won't have access at this layer - expect(hasAccess).toBe(false); - - // The actual admin bypass happens in the middleware layer (`canAccessPromptViaGroup`/`canAccessPromptGroupResource`) - // which checks req.user.role === SystemRoles.ADMIN - }); - }); - - describe('Group-based Access', () => { - let testPromptGroup; - - beforeEach(async () => { - // Create a prompt group first - testPromptGroup = await PromptGroup.create({ - name: 'Group Access Test Group', - author: testUsers.owner._id, - authorName: testUsers.owner.name, - productionId: new ObjectId(), - }); - - await Prompt.create({ - prompt: 'Group access test prompt', - name: 'Group Test', - author: testUsers.owner._id, - groupId: testPromptGroup._id, - type: 'text', - }); - - // Add users to groups - await User.findByIdAndUpdate(testUsers.editor._id, { - $push: { groups: testGroups.editors._id }, - }); - - await User.findByIdAndUpdate(testUsers.viewer._id, { - $push: { groups: testGroups.viewers._id }, - }); - }); - - afterEach(async () => { - await Prompt.deleteMany({}); - await AclEntry.deleteMany({}); - await User.updateMany({}, { $set: { groups: [] } }); - }); - - it('group members should inherit group permissions', async () => { - // Create a prompt group - const testPromptGroup = await PromptGroup.create({ - name: 'Group Test Group', - author: testUsers.owner._id, - authorName: testUsers.owner.name, - productionId: new ObjectId(), - }); - - const { addUserToGroup } = require('~/models'); - await addUserToGroup(testUsers.editor._id, testGroups.editors._id); - - const prompt = await promptFns.savePrompt({ - author: testUsers.owner._id, - prompt: { - prompt: 'Group test prompt', - name: 'Group Test', - groupId: testPromptGroup._id, - type: 'text', - }, - }); - - // Check if savePrompt returned an error - if (!prompt || !prompt.prompt) { - throw new Error(`Failed to save prompt: ${prompt?.message || 'Unknown error'}`); - } - - // Grant edit permissions to the group - await permissionService.grantPermission({ - principalType: PrincipalType.GROUP, - principalId: testGroups.editors._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - accessRoleId: AccessRoleIds.PROMPTGROUP_EDITOR, - grantedBy: testUsers.owner._id, - }); - - // Check if group member has access - const hasAccess = await permissionService.checkPermission({ - userId: testUsers.editor._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - requiredPermission: PermissionBits.EDIT, - }); - - expect(hasAccess).toBe(true); - - // Check that non-member doesn't have access - const nonMemberAccess = await permissionService.checkPermission({ - userId: testUsers.viewer._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - requiredPermission: PermissionBits.EDIT, - }); - - expect(nonMemberAccess).toBe(false); - }); - }); - - describe('Public Access', () => { - let publicPromptGroup, privatePromptGroup; - - beforeEach(async () => { - // Create separate prompt groups for public and private access - publicPromptGroup = await PromptGroup.create({ - name: 'Public Access Test Group', - author: testUsers.owner._id, - authorName: testUsers.owner.name, - productionId: new ObjectId(), - }); - - privatePromptGroup = await PromptGroup.create({ - name: 'Private Access Test Group', - author: testUsers.owner._id, - authorName: testUsers.owner.name, - productionId: new ObjectId(), - }); - - // Create prompts in their respective groups - await Prompt.create({ - prompt: 'Public prompt', - name: 'Public', - author: testUsers.owner._id, - groupId: publicPromptGroup._id, - type: 'text', - }); - - await Prompt.create({ - prompt: 'Private prompt', - name: 'Private', - author: testUsers.owner._id, - groupId: privatePromptGroup._id, - type: 'text', - }); - - // Grant public view access to publicPromptGroup - await permissionService.grantPermission({ - principalType: PrincipalType.PUBLIC, - principalId: null, - resourceType: ResourceType.PROMPTGROUP, - resourceId: publicPromptGroup._id, - accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER, - grantedBy: testUsers.owner._id, - }); - - // Grant only owner access to privatePromptGroup - await permissionService.grantPermission({ - principalType: PrincipalType.USER, - principalId: testUsers.owner._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: privatePromptGroup._id, - accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER, - grantedBy: testUsers.owner._id, - }); - }); - - afterEach(async () => { - await Prompt.deleteMany({}); - await PromptGroup.deleteMany({}); - await AclEntry.deleteMany({}); - }); - - it('public prompt should be accessible to any user', async () => { - const hasAccess = await permissionService.checkPermission({ - userId: testUsers.noAccess._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: publicPromptGroup._id, - requiredPermission: PermissionBits.VIEW, - includePublic: true, - }); - - expect(hasAccess).toBe(true); - }); - - it('private prompt should not be accessible to unauthorized users', async () => { - const hasAccess = await permissionService.checkPermission({ - userId: testUsers.noAccess._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: privatePromptGroup._id, - requiredPermission: PermissionBits.VIEW, - includePublic: true, - }); - - expect(hasAccess).toBe(false); - }); - }); - - describe('Prompt Deletion', () => { - let testPromptGroup; - - it('should remove ACL entries when prompt is deleted', async () => { - testPromptGroup = await PromptGroup.create({ - name: 'Deletion Test Group', - author: testUsers.owner._id, - authorName: testUsers.owner.name, - productionId: new ObjectId(), - }); - - const prompt = await promptFns.savePrompt({ - author: testUsers.owner._id, - prompt: { - prompt: 'To be deleted', - name: 'Delete Test', - groupId: testPromptGroup._id, - type: 'text', - }, - }); - - // Check if savePrompt returned an error - if (!prompt || !prompt.prompt) { - throw new Error(`Failed to save prompt: ${prompt?.message || 'Unknown error'}`); - } - - const testPromptId = prompt.prompt._id; - const promptGroupId = testPromptGroup._id; - - // Grant permission - await permissionService.grantPermission({ - principalType: PrincipalType.USER, - principalId: testUsers.owner._id, - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - accessRoleId: AccessRoleIds.PROMPTGROUP_OWNER, - grantedBy: testUsers.owner._id, - }); - - // Verify ACL entry exists - const beforeDelete = await AclEntry.find({ - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - }); - expect(beforeDelete).toHaveLength(1); - - // Delete the prompt - await promptFns.deletePrompt({ - promptId: testPromptId, - groupId: promptGroupId, - author: testUsers.owner._id, - role: SystemRoles.USER, - }); - - // Verify ACL entries are removed - const aclEntries = await AclEntry.find({ - resourceType: ResourceType.PROMPTGROUP, - resourceId: testPromptGroup._id, - }); - - expect(aclEntries).toHaveLength(0); - }); - }); - - describe('Backwards Compatibility', () => { - it('should handle prompts without ACL entries gracefully', async () => { - // Create a prompt group first - const promptGroup = await PromptGroup.create({ - name: 'Legacy Test Group', - author: testUsers.owner._id, - authorName: testUsers.owner.name, - productionId: new ObjectId(), - }); - - // Create a prompt without ACL entries (legacy prompt) - const legacyPrompt = await Prompt.create({ - prompt: 'Legacy prompt without ACL', - name: 'Legacy', - author: testUsers.owner._id, - groupId: promptGroup._id, - type: 'text', - }); - - // The system should handle this gracefully - const prompt = await promptFns.getPrompt({ _id: legacyPrompt._id }); - expect(prompt).toBeTruthy(); - expect(prompt._id.toString()).toBe(legacyPrompt._id.toString()); - }); - }); -}); diff --git a/api/models/Role.js b/api/models/Role.js deleted file mode 100644 index 1766dc9b08..0000000000 --- a/api/models/Role.js +++ /dev/null @@ -1,256 +0,0 @@ -const { - CacheKeys, - SystemRoles, - roleDefaults, - permissionsSchema, - removeNullishValues, -} = require('librechat-data-provider'); -const { logger } = require('@librechat/data-schemas'); -const getLogStores = require('~/cache/getLogStores'); -const { Role } = require('~/db/models'); - -/** - * Retrieve a role by name and convert the found role document to a plain object. - * If the role with the given name doesn't exist and the name is a system defined role, - * create it and return the lean version. - * - * @param {string} roleName - The name of the role to find or create. - * @param {string|string[]} [fieldsToSelect] - The fields to include or exclude in the returned document. - * @returns {Promise} Role document. - */ -const getRoleByName = async function (roleName, fieldsToSelect = null) { - const cache = getLogStores(CacheKeys.ROLES); - try { - const cachedRole = await cache.get(roleName); - if (cachedRole) { - return cachedRole; - } - let query = Role.findOne({ name: roleName }); - if (fieldsToSelect) { - query = query.select(fieldsToSelect); - } - let role = await query.lean().exec(); - - if (!role && SystemRoles[roleName]) { - role = await new Role(roleDefaults[roleName]).save(); - await cache.set(roleName, role); - return role.toObject(); - } - await cache.set(roleName, role); - return role; - } catch (error) { - throw new Error(`Failed to retrieve or create role: ${error.message}`); - } -}; - -/** - * Update role values by name. - * - * @param {string} roleName - The name of the role to update. - * @param {Partial} updates - The fields to update. - * @returns {Promise} Updated role document. - */ -const updateRoleByName = async function (roleName, updates) { - const cache = getLogStores(CacheKeys.ROLES); - try { - const role = await Role.findOneAndUpdate( - { name: roleName }, - { $set: updates }, - { new: true, lean: true }, - ) - .select('-__v') - .lean() - .exec(); - await cache.set(roleName, role); - return role; - } catch (error) { - throw new Error(`Failed to update role: ${error.message}`); - } -}; - -/** - * Updates access permissions for a specific role and multiple permission types. - * @param {string} roleName - The role to update. - * @param {Object.>} permissionsUpdate - Permissions to update and their values. - * @param {IRole} [roleData] - Optional role data to use instead of fetching from the database. - */ -async function updateAccessPermissions(roleName, permissionsUpdate, roleData) { - // Filter and clean the permission updates based on our schema definition. - const updates = {}; - for (const [permissionType, permissions] of Object.entries(permissionsUpdate)) { - if (permissionsSchema.shape && permissionsSchema.shape[permissionType]) { - updates[permissionType] = removeNullishValues(permissions); - } - } - if (!Object.keys(updates).length) { - return; - } - - try { - const role = roleData ?? (await getRoleByName(roleName)); - if (!role) { - return; - } - - const currentPermissions = role.permissions || {}; - const updatedPermissions = { ...currentPermissions }; - let hasChanges = false; - - const unsetFields = {}; - const permissionTypes = Object.keys(permissionsSchema.shape || {}); - for (const permType of permissionTypes) { - if (role[permType] && typeof role[permType] === 'object') { - logger.info( - `Migrating '${roleName}' role from old schema: found '${permType}' at top level`, - ); - - updatedPermissions[permType] = { - ...updatedPermissions[permType], - ...role[permType], - }; - - unsetFields[permType] = 1; - hasChanges = true; - } - } - - for (const [permissionType, permissions] of Object.entries(updates)) { - const currentTypePermissions = currentPermissions[permissionType] || {}; - updatedPermissions[permissionType] = { ...currentTypePermissions }; - - for (const [permission, value] of Object.entries(permissions)) { - if (currentTypePermissions[permission] !== value) { - updatedPermissions[permissionType][permission] = value; - hasChanges = true; - logger.info( - `Updating '${roleName}' role permission '${permissionType}' '${permission}' from ${currentTypePermissions[permission]} to: ${value}`, - ); - } - } - } - - if (hasChanges) { - const updateObj = { permissions: updatedPermissions }; - - if (Object.keys(unsetFields).length > 0) { - logger.info( - `Unsetting old schema fields for '${roleName}' role: ${Object.keys(unsetFields).join(', ')}`, - ); - - try { - await Role.updateOne( - { name: roleName }, - { - $set: updateObj, - $unset: unsetFields, - }, - ); - - const cache = getLogStores(CacheKeys.ROLES); - const updatedRole = await Role.findOne({ name: roleName }).select('-__v').lean().exec(); - await cache.set(roleName, updatedRole); - - logger.info(`Updated role '${roleName}' and removed old schema fields`); - } catch (updateError) { - logger.error(`Error during role migration update: ${updateError.message}`); - throw updateError; - } - } else { - // Standard update if no migration needed - await updateRoleByName(roleName, updateObj); - } - - logger.info(`Updated '${roleName}' role permissions`); - } else { - logger.info(`No changes needed for '${roleName}' role permissions`); - } - } catch (error) { - logger.error(`Failed to update ${roleName} role permissions:`, error); - } -} - -/** - * Migrates roles from old schema to new schema structure. - * This can be called directly to fix existing roles. - * - * @param {string} [roleName] - Optional specific role to migrate. If not provided, migrates all roles. - * @returns {Promise} Number of roles migrated. - */ -const migrateRoleSchema = async function (roleName) { - try { - // Get roles to migrate - let roles; - if (roleName) { - const role = await Role.findOne({ name: roleName }); - roles = role ? [role] : []; - } else { - roles = await Role.find({}); - } - - logger.info(`Migrating ${roles.length} roles to new schema structure`); - let migratedCount = 0; - - for (const role of roles) { - const permissionTypes = Object.keys(permissionsSchema.shape || {}); - const unsetFields = {}; - let hasOldSchema = false; - - // Check for old schema fields - for (const permType of permissionTypes) { - if (role[permType] && typeof role[permType] === 'object') { - hasOldSchema = true; - - // Ensure permissions object exists - role.permissions = role.permissions || {}; - - // Migrate permissions from old location to new - role.permissions[permType] = { - ...role.permissions[permType], - ...role[permType], - }; - - // Mark field for removal - unsetFields[permType] = 1; - } - } - - if (hasOldSchema) { - try { - logger.info(`Migrating role '${role.name}' from old schema structure`); - - // Simple update operation - await Role.updateOne( - { _id: role._id }, - { - $set: { permissions: role.permissions }, - $unset: unsetFields, - }, - ); - - // Refresh cache - const cache = getLogStores(CacheKeys.ROLES); - const updatedRole = await Role.findById(role._id).lean().exec(); - await cache.set(role.name, updatedRole); - - migratedCount++; - logger.info(`Migrated role '${role.name}'`); - } catch (error) { - logger.error(`Failed to migrate role '${role.name}': ${error.message}`); - } - } - } - - logger.info(`Migration complete: ${migratedCount} roles migrated`); - return migratedCount; - } catch (error) { - logger.error(`Role schema migration failed: ${error.message}`); - throw error; - } -}; - -module.exports = { - getRoleByName, - updateRoleByName, - migrateRoleSchema, - updateAccessPermissions, -}; diff --git a/api/models/ToolCall.js b/api/models/ToolCall.js deleted file mode 100644 index 689386114b..0000000000 --- a/api/models/ToolCall.js +++ /dev/null @@ -1,96 +0,0 @@ -const { ToolCall } = require('~/db/models'); - -/** - * Create a new tool call - * @param {IToolCallData} toolCallData - The tool call data - * @returns {Promise} The created tool call document - */ -async function createToolCall(toolCallData) { - try { - return await ToolCall.create(toolCallData); - } catch (error) { - throw new Error(`Error creating tool call: ${error.message}`); - } -} - -/** - * Get a tool call by ID - * @param {string} id - The tool call document ID - * @returns {Promise} The tool call document or null if not found - */ -async function getToolCallById(id) { - try { - return await ToolCall.findById(id).lean(); - } catch (error) { - throw new Error(`Error fetching tool call: ${error.message}`); - } -} - -/** - * Get tool calls by message ID and user - * @param {string} messageId - The message ID - * @param {string} userId - The user's ObjectId - * @returns {Promise} Array of tool call documents - */ -async function getToolCallsByMessage(messageId, userId) { - try { - return await ToolCall.find({ messageId, user: userId }).lean(); - } catch (error) { - throw new Error(`Error fetching tool calls: ${error.message}`); - } -} - -/** - * Get tool calls by conversation ID and user - * @param {string} conversationId - The conversation ID - * @param {string} userId - The user's ObjectId - * @returns {Promise} Array of tool call documents - */ -async function getToolCallsByConvo(conversationId, userId) { - try { - return await ToolCall.find({ conversationId, user: userId }).lean(); - } catch (error) { - throw new Error(`Error fetching tool calls: ${error.message}`); - } -} - -/** - * Update a tool call - * @param {string} id - The tool call document ID - * @param {Partial} updateData - The data to update - * @returns {Promise} The updated tool call document or null if not found - */ -async function updateToolCall(id, updateData) { - try { - return await ToolCall.findByIdAndUpdate(id, updateData, { new: true }).lean(); - } catch (error) { - throw new Error(`Error updating tool call: ${error.message}`); - } -} - -/** - * Delete a tool call - * @param {string} userId - The related user's ObjectId - * @param {string} [conversationId] - The tool call conversation ID - * @returns {Promise<{ ok?: number; n?: number; deletedCount?: number }>} The result of the delete operation - */ -async function deleteToolCalls(userId, conversationId) { - try { - const query = { user: userId }; - if (conversationId) { - query.conversationId = conversationId; - } - return await ToolCall.deleteMany(query); - } catch (error) { - throw new Error(`Error deleting tool call: ${error.message}`); - } -} - -module.exports = { - createToolCall, - updateToolCall, - deleteToolCalls, - getToolCallById, - getToolCallsByConvo, - getToolCallsByMessage, -}; diff --git a/api/models/Transaction.js b/api/models/Transaction.js deleted file mode 100644 index e553e2bb3b..0000000000 --- a/api/models/Transaction.js +++ /dev/null @@ -1,356 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); -const { getMultiplier, getCacheMultiplier } = require('./tx'); -const { Transaction, Balance } = require('~/db/models'); - -const cancelRate = 1.15; - -/** - * Updates a user's token balance based on a transaction using optimistic concurrency control - * without schema changes. Compatible with DocumentDB. - * @async - * @function - * @param {Object} params - The function parameters. - * @param {string|mongoose.Types.ObjectId} params.user - The user ID. - * @param {number} params.incrementValue - The value to increment the balance by (can be negative). - * @param {import('mongoose').UpdateQuery['$set']} [params.setValues] - Optional additional fields to set. - * @returns {Promise} Returns the updated balance document (lean). - * @throws {Error} Throws an error if the update fails after multiple retries. - */ -const updateBalance = async ({ user, incrementValue, setValues }) => { - let maxRetries = 10; // Number of times to retry on conflict - let delay = 50; // Initial retry delay in ms - let lastError = null; - - for (let attempt = 1; attempt <= maxRetries; attempt++) { - let currentBalanceDoc; - try { - // 1. Read the current document state - currentBalanceDoc = await Balance.findOne({ user }).lean(); - const currentCredits = currentBalanceDoc ? currentBalanceDoc.tokenCredits : 0; - - // 2. Calculate the desired new state - const potentialNewCredits = currentCredits + incrementValue; - const newCredits = Math.max(0, potentialNewCredits); // Ensure balance doesn't go below zero - - // 3. Prepare the update payload - const updatePayload = { - $set: { - tokenCredits: newCredits, - ...(setValues || {}), // Merge other values to set - }, - }; - - // 4. Attempt the conditional update or upsert - let updatedBalance = null; - if (currentBalanceDoc) { - // --- Document Exists: Perform Conditional Update --- - // Try to update only if the tokenCredits match the value we read (currentCredits) - updatedBalance = await Balance.findOneAndUpdate( - { - user: user, - tokenCredits: currentCredits, // Optimistic lock: condition based on the read value - }, - updatePayload, - { - new: true, // Return the modified document - // lean: true, // .lean() is applied after query execution in Mongoose >= 6 - }, - ).lean(); // Use lean() for plain JS object - - if (updatedBalance) { - // Success! The update was applied based on the expected current state. - return updatedBalance; - } - // If updatedBalance is null, it means tokenCredits changed between read and write (conflict). - lastError = new Error(`Concurrency conflict for user ${user} on attempt ${attempt}.`); - // Proceed to retry logic below. - } else { - // --- Document Does Not Exist: Perform Conditional Upsert --- - // Try to insert the document, but only if it still doesn't exist. - // Using tokenCredits: {$exists: false} helps prevent race conditions where - // another process creates the doc between our findOne and findOneAndUpdate. - try { - updatedBalance = await Balance.findOneAndUpdate( - { - user: user, - // Attempt to match only if the document doesn't exist OR was just created - // without tokenCredits (less likely but possible). A simple { user } filter - // might also work, relying on the retry for conflicts. - // Let's use a simpler filter and rely on retry for races. - // tokenCredits: { $exists: false } // This condition might be too strict if doc exists with 0 credits - }, - updatePayload, - { - upsert: true, // Create if doesn't exist - new: true, // Return the created/updated document - // setDefaultsOnInsert: true, // Ensure schema defaults are applied on insert - // lean: true, - }, - ).lean(); - - if (updatedBalance) { - // Upsert succeeded (likely created the document) - return updatedBalance; - } - // If null, potentially a rare race condition during upsert. Retry should handle it. - lastError = new Error( - `Upsert race condition suspected for user ${user} on attempt ${attempt}.`, - ); - } catch (error) { - if (error.code === 11000) { - // E11000 duplicate key error on index - // This means another process created the document *just* before our upsert. - // It's a concurrency conflict during creation. We should retry. - lastError = error; // Store the error - // Proceed to retry logic below. - } else { - // Different error, rethrow - throw error; - } - } - } // End if/else (document exists?) - } catch (error) { - // Catch errors from findOne or unexpected findOneAndUpdate errors - logger.error(`[updateBalance] Error during attempt ${attempt} for user ${user}:`, error); - lastError = error; // Store the error - // Consider stopping retries for non-transient errors, but for now, we retry. - } - - // If we reached here, it means the update failed (conflict or error), wait and retry - if (attempt < maxRetries) { - const jitter = Math.random() * delay * 0.5; // Add jitter to delay - await new Promise((resolve) => setTimeout(resolve, delay + jitter)); - delay = Math.min(delay * 2, 2000); // Exponential backoff with cap - } - } // End for loop (retries) - - // If loop finishes without success, throw the last encountered error or a generic one - logger.error( - `[updateBalance] Failed to update balance for user ${user} after ${maxRetries} attempts.`, - ); - throw ( - lastError || - new Error( - `Failed to update balance for user ${user} after maximum retries due to persistent conflicts.`, - ) - ); -}; - -/** Method to calculate and set the tokenValue for a transaction */ -function calculateTokenValue(txn) { - const { valueKey, tokenType, model, endpointTokenConfig, inputTokenCount } = txn; - const multiplier = Math.abs( - getMultiplier({ valueKey, tokenType, model, endpointTokenConfig, inputTokenCount }), - ); - txn.rate = multiplier; - txn.tokenValue = txn.rawAmount * multiplier; - if (txn.context && txn.tokenType === 'completion' && txn.context === 'incomplete') { - txn.tokenValue = Math.ceil(txn.tokenValue * cancelRate); - txn.rate *= cancelRate; - } -} - -/** - * New static method to create an auto-refill transaction that does NOT trigger a balance update. - * @param {object} txData - Transaction data. - * @param {string} txData.user - The user ID. - * @param {string} txData.tokenType - The type of token. - * @param {string} txData.context - The context of the transaction. - * @param {number} txData.rawAmount - The raw amount of tokens. - * @returns {Promise} - The created transaction. - */ -async function createAutoRefillTransaction(txData) { - if (txData.rawAmount != null && isNaN(txData.rawAmount)) { - return; - } - const transaction = new Transaction(txData); - transaction.endpointTokenConfig = txData.endpointTokenConfig; - transaction.inputTokenCount = txData.inputTokenCount; - calculateTokenValue(transaction); - await transaction.save(); - - const balanceResponse = await updateBalance({ - user: transaction.user, - incrementValue: txData.rawAmount, - setValues: { lastRefill: new Date() }, - }); - const result = { - rate: transaction.rate, - user: transaction.user.toString(), - balance: balanceResponse.tokenCredits, - }; - logger.debug('[Balance.check] Auto-refill performed', result); - result.transaction = transaction; - return result; -} - -/** - * Static method to create a transaction and update the balance - * @param {txData} _txData - Transaction data. - */ -async function createTransaction(_txData) { - const { balance, transactions, ...txData } = _txData; - if (txData.rawAmount != null && isNaN(txData.rawAmount)) { - return; - } - - if (transactions?.enabled === false) { - return; - } - - const transaction = new Transaction(txData); - transaction.endpointTokenConfig = txData.endpointTokenConfig; - transaction.inputTokenCount = txData.inputTokenCount; - calculateTokenValue(transaction); - - await transaction.save(); - if (!balance?.enabled) { - return; - } - - let incrementValue = transaction.tokenValue; - const balanceResponse = await updateBalance({ - user: transaction.user, - incrementValue, - }); - - return { - rate: transaction.rate, - user: transaction.user.toString(), - balance: balanceResponse.tokenCredits, - [transaction.tokenType]: incrementValue, - }; -} - -/** - * Static method to create a structured transaction and update the balance - * @param {txData} _txData - Transaction data. - */ -async function createStructuredTransaction(_txData) { - const { balance, transactions, ...txData } = _txData; - if (transactions?.enabled === false) { - return; - } - - const transaction = new Transaction(txData); - transaction.endpointTokenConfig = txData.endpointTokenConfig; - transaction.inputTokenCount = txData.inputTokenCount; - - calculateStructuredTokenValue(transaction); - - await transaction.save(); - - if (!balance?.enabled) { - return; - } - - let incrementValue = transaction.tokenValue; - - const balanceResponse = await updateBalance({ - user: transaction.user, - incrementValue, - }); - - return { - rate: transaction.rate, - user: transaction.user.toString(), - balance: balanceResponse.tokenCredits, - [transaction.tokenType]: incrementValue, - }; -} - -/** Method to calculate token value for structured tokens */ -function calculateStructuredTokenValue(txn) { - if (!txn.tokenType) { - txn.tokenValue = txn.rawAmount; - return; - } - - const { model, endpointTokenConfig, inputTokenCount } = txn; - - if (txn.tokenType === 'prompt') { - const inputMultiplier = getMultiplier({ - tokenType: 'prompt', - model, - endpointTokenConfig, - inputTokenCount, - }); - const writeMultiplier = - getCacheMultiplier({ cacheType: 'write', model, endpointTokenConfig }) ?? inputMultiplier; - const readMultiplier = - getCacheMultiplier({ cacheType: 'read', model, endpointTokenConfig }) ?? inputMultiplier; - - txn.rateDetail = { - input: inputMultiplier, - write: writeMultiplier, - read: readMultiplier, - }; - - const totalPromptTokens = - Math.abs(txn.inputTokens || 0) + - Math.abs(txn.writeTokens || 0) + - Math.abs(txn.readTokens || 0); - - if (totalPromptTokens > 0) { - txn.rate = - (Math.abs(inputMultiplier * (txn.inputTokens || 0)) + - Math.abs(writeMultiplier * (txn.writeTokens || 0)) + - Math.abs(readMultiplier * (txn.readTokens || 0))) / - totalPromptTokens; - } else { - txn.rate = Math.abs(inputMultiplier); // Default to input rate if no tokens - } - - txn.tokenValue = -( - Math.abs(txn.inputTokens || 0) * inputMultiplier + - Math.abs(txn.writeTokens || 0) * writeMultiplier + - Math.abs(txn.readTokens || 0) * readMultiplier - ); - - txn.rawAmount = -totalPromptTokens; - } else if (txn.tokenType === 'completion') { - const multiplier = getMultiplier({ - tokenType: txn.tokenType, - model, - endpointTokenConfig, - inputTokenCount, - }); - txn.rate = Math.abs(multiplier); - txn.tokenValue = -Math.abs(txn.rawAmount) * multiplier; - txn.rawAmount = -Math.abs(txn.rawAmount); - } - - if (txn.context && txn.tokenType === 'completion' && txn.context === 'incomplete') { - txn.tokenValue = Math.ceil(txn.tokenValue * cancelRate); - txn.rate *= cancelRate; - if (txn.rateDetail) { - txn.rateDetail = Object.fromEntries( - Object.entries(txn.rateDetail).map(([k, v]) => [k, v * cancelRate]), - ); - } - } -} - -/** - * Queries and retrieves transactions based on a given filter. - * @async - * @function getTransactions - * @param {Object} filter - MongoDB filter object to apply when querying transactions. - * @returns {Promise} A promise that resolves to an array of matched transactions. - * @throws {Error} Throws an error if querying the database fails. - */ -async function getTransactions(filter) { - try { - return await Transaction.find(filter).lean(); - } catch (error) { - logger.error('Error querying transactions:', error); - throw error; - } -} - -module.exports = { - getTransactions, - createTransaction, - createAutoRefillTransaction, - createStructuredTransaction, -}; diff --git a/api/models/balanceMethods.js b/api/models/balanceMethods.js deleted file mode 100644 index e614872eac..0000000000 --- a/api/models/balanceMethods.js +++ /dev/null @@ -1,156 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); -const { ViolationTypes } = require('librechat-data-provider'); -const { createAutoRefillTransaction } = require('./Transaction'); -const { logViolation } = require('~/cache'); -const { getMultiplier } = require('./tx'); -const { Balance } = require('~/db/models'); - -function isInvalidDate(date) { - return isNaN(date); -} - -/** - * Simple check method that calculates token cost and returns balance info. - * The auto-refill logic has been moved to balanceMethods.js to prevent circular dependencies. - */ -const checkBalanceRecord = async function ({ - user, - model, - endpoint, - valueKey, - tokenType, - amount, - endpointTokenConfig, -}) { - const multiplier = getMultiplier({ valueKey, tokenType, model, endpoint, endpointTokenConfig }); - const tokenCost = amount * multiplier; - - // Retrieve the balance record - let record = await Balance.findOne({ user }).lean(); - if (!record) { - logger.debug('[Balance.check] No balance record found for user', { user }); - return { - canSpend: false, - balance: 0, - tokenCost, - }; - } - let balance = record.tokenCredits; - - logger.debug('[Balance.check] Initial state', { - user, - model, - endpoint, - valueKey, - tokenType, - amount, - balance, - multiplier, - endpointTokenConfig: !!endpointTokenConfig, - }); - - // Only perform auto-refill if spending would bring the balance to 0 or below - if (balance - tokenCost <= 0 && record.autoRefillEnabled && record.refillAmount > 0) { - const lastRefillDate = new Date(record.lastRefill); - const now = new Date(); - if ( - isInvalidDate(lastRefillDate) || - now >= - addIntervalToDate(lastRefillDate, record.refillIntervalValue, record.refillIntervalUnit) - ) { - try { - /** @type {{ rate: number, user: string, balance: number, transaction: import('@librechat/data-schemas').ITransaction}} */ - const result = await createAutoRefillTransaction({ - user: user, - tokenType: 'credits', - context: 'autoRefill', - rawAmount: record.refillAmount, - }); - balance = result.balance; - } catch (error) { - logger.error('[Balance.check] Failed to record transaction for auto-refill', error); - } - } - } - - logger.debug('[Balance.check] Token cost', { tokenCost }); - return { canSpend: balance >= tokenCost, balance, tokenCost }; -}; - -/** - * Adds a time interval to a given date. - * @param {Date} date - The starting date. - * @param {number} value - The numeric value of the interval. - * @param {'seconds'|'minutes'|'hours'|'days'|'weeks'|'months'} unit - The unit of time. - * @returns {Date} A new Date representing the starting date plus the interval. - */ -const addIntervalToDate = (date, value, unit) => { - const result = new Date(date); - switch (unit) { - case 'seconds': - result.setSeconds(result.getSeconds() + value); - break; - case 'minutes': - result.setMinutes(result.getMinutes() + value); - break; - case 'hours': - result.setHours(result.getHours() + value); - break; - case 'days': - result.setDate(result.getDate() + value); - break; - case 'weeks': - result.setDate(result.getDate() + value * 7); - break; - case 'months': - result.setMonth(result.getMonth() + value); - break; - default: - break; - } - return result; -}; - -/** - * Checks the balance for a user and determines if they can spend a certain amount. - * If the user cannot spend the amount, it logs a violation and denies the request. - * - * @async - * @function - * @param {Object} params - The function parameters. - * @param {ServerRequest} params.req - The Express request object. - * @param {Express.Response} params.res - The Express response object. - * @param {Object} params.txData - The transaction data. - * @param {string} params.txData.user - The user ID or identifier. - * @param {('prompt' | 'completion')} params.txData.tokenType - The type of token. - * @param {number} params.txData.amount - The amount of tokens. - * @param {string} params.txData.model - The model name or identifier. - * @param {string} [params.txData.endpointTokenConfig] - The token configuration for the endpoint. - * @returns {Promise} Throws error if the user cannot spend the amount. - * @throws {Error} Throws an error if there's an issue with the balance check. - */ -const checkBalance = async ({ req, res, txData }) => { - const { canSpend, balance, tokenCost } = await checkBalanceRecord(txData); - if (canSpend) { - return true; - } - - const type = ViolationTypes.TOKEN_BALANCE; - const errorMessage = { - type, - balance, - tokenCost, - promptTokens: txData.amount, - }; - - if (txData.generations && txData.generations.length > 0) { - errorMessage.generations = txData.generations; - } - - await logViolation(req, res, type, errorMessage, 0); - throw new Error(JSON.stringify(errorMessage)); -}; - -module.exports = { - checkBalance, -}; diff --git a/api/models/index.js b/api/models/index.js index d0b10be079..2a1cb222f9 100644 --- a/api/models/index.js +++ b/api/models/index.js @@ -1,48 +1,22 @@ const mongoose = require('mongoose'); const { createMethods } = require('@librechat/data-schemas'); -const methods = createMethods(mongoose); -const { comparePassword } = require('./userMethods'); -const { - getMessage, - getMessages, - saveMessage, - recordMessage, - updateMessage, - deleteMessagesSince, - deleteMessages, -} = require('./Message'); -const { getConvoTitle, getConvo, saveConvo, deleteConvos } = require('./Conversation'); -const { getPreset, getPresets, savePreset, deletePresets } = require('./Preset'); -const { File } = require('~/db/models'); +const { matchModelName, findMatchingPattern } = require('@librechat/api'); +const getLogStores = require('~/cache/getLogStores'); + +const methods = createMethods(mongoose, { + matchModelName, + findMatchingPattern, + getCache: getLogStores, +}); const seedDatabase = async () => { await methods.initializeRoles(); await methods.seedDefaultRoles(); await methods.ensureDefaultCategories(); + await methods.seedSystemGrants(); }; module.exports = { ...methods, seedDatabase, - comparePassword, - - getMessage, - getMessages, - saveMessage, - recordMessage, - updateMessage, - deleteMessagesSince, - deleteMessages, - - getConvoTitle, - getConvo, - saveConvo, - deleteConvos, - - getPreset, - getPresets, - savePreset, - deletePresets, - - Files: File, }; diff --git a/api/models/interface.js b/api/models/interface.js deleted file mode 100644 index a79a8e747f..0000000000 --- a/api/models/interface.js +++ /dev/null @@ -1,24 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); -const { updateInterfacePermissions: updateInterfacePerms } = require('@librechat/api'); -const { getRoleByName, updateAccessPermissions } = require('./Role'); - -/** - * Update interface permissions based on app configuration. - * Must be done independently from loading the app config. - * @param {AppConfig} appConfig - */ -async function updateInterfacePermissions(appConfig) { - try { - await updateInterfacePerms({ - appConfig, - getRoleByName, - updateAccessPermissions, - }); - } catch (error) { - logger.error('Error updating interface permissions:', error); - } -} - -module.exports = { - updateInterfacePermissions, -}; diff --git a/api/models/inviteUser.js b/api/models/inviteUser.js deleted file mode 100644 index eda8394225..0000000000 --- a/api/models/inviteUser.js +++ /dev/null @@ -1,68 +0,0 @@ -const mongoose = require('mongoose'); -const { logger, hashToken, getRandomValues } = require('@librechat/data-schemas'); -const { createToken, findToken } = require('~/models'); - -/** - * @module inviteUser - * @description This module provides functions to create and get user invites - */ - -/** - * @function createInvite - * @description This function creates a new user invite - * @param {string} email - The email of the user to invite - * @returns {Promise} A promise that resolves to the saved invite document - * @throws {Error} If there is an error creating the invite - */ -const createInvite = async (email) => { - try { - const token = await getRandomValues(32); - const hash = await hashToken(token); - const encodedToken = encodeURIComponent(token); - - const fakeUserId = new mongoose.Types.ObjectId(); - - await createToken({ - userId: fakeUserId, - email, - token: hash, - createdAt: Date.now(), - expiresIn: 604800, - }); - - return encodedToken; - } catch (error) { - logger.error('[createInvite] Error creating invite', error); - return { message: 'Error creating invite' }; - } -}; - -/** - * @function getInvite - * @description This function retrieves a user invite - * @param {string} encodedToken - The token of the invite to retrieve - * @param {string} email - The email of the user to validate - * @returns {Promise} A promise that resolves to the retrieved invite document - * @throws {Error} If there is an error retrieving the invite, if the invite does not exist, or if the email does not match - */ -const getInvite = async (encodedToken, email) => { - try { - const token = decodeURIComponent(encodedToken); - const hash = await hashToken(token); - const invite = await findToken({ token: hash, email }); - - if (!invite) { - throw new Error('Invite not found or email does not match'); - } - - return invite; - } catch (error) { - logger.error('[getInvite] Error getting invite:', error); - return { error: true, message: error.message }; - } -}; - -module.exports = { - createInvite, - getInvite, -}; diff --git a/api/models/loadAddedAgent.js b/api/models/loadAddedAgent.js deleted file mode 100644 index aa83375eae..0000000000 --- a/api/models/loadAddedAgent.js +++ /dev/null @@ -1,218 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); -const { getCustomEndpointConfig } = require('@librechat/api'); -const { - Tools, - Constants, - isAgentsEndpoint, - isEphemeralAgentId, - appendAgentIdSuffix, - encodeEphemeralAgentId, -} = require('librechat-data-provider'); -const { getMCPServerTools } = require('~/server/services/Config'); - -const { mcp_all, mcp_delimiter } = Constants; - -/** - * Constant for added conversation agent ID - */ -const ADDED_AGENT_ID = 'added_agent'; - -/** - * Get an agent document based on the provided ID. - * @param {Object} searchParameter - The search parameters to find the agent. - * @param {string} searchParameter.id - The ID of the agent. - * @returns {Promise} - */ -let getAgent; - -/** - * Set the getAgent function (dependency injection to avoid circular imports) - * @param {Function} fn - */ -const setGetAgent = (fn) => { - getAgent = fn; -}; - -/** - * Load an agent from an added conversation (TConversation). - * Used for multi-convo parallel agent execution. - * - * @param {Object} params - * @param {import('express').Request} params.req - * @param {import('librechat-data-provider').TConversation} params.conversation - The added conversation - * @param {import('librechat-data-provider').Agent} [params.primaryAgent] - The primary agent (used to duplicate tools when both are ephemeral) - * @returns {Promise} The agent config as a plain object, or null if invalid. - */ -const loadAddedAgent = async ({ req, conversation, primaryAgent }) => { - if (!conversation) { - return null; - } - - // If there's an agent_id, load the existing agent - if (conversation.agent_id && !isEphemeralAgentId(conversation.agent_id)) { - if (!getAgent) { - throw new Error('getAgent not initialized - call setGetAgent first'); - } - const agent = await getAgent({ - id: conversation.agent_id, - }); - - if (!agent) { - logger.warn(`[loadAddedAgent] Agent ${conversation.agent_id} not found`); - return null; - } - - agent.version = agent.versions ? agent.versions.length : 0; - // Append suffix to distinguish from primary agent (matches ephemeral format) - // This is needed when both agents have the same ID or for consistent parallel content attribution - agent.id = appendAgentIdSuffix(agent.id, 1); - return agent; - } - - // Otherwise, create an ephemeral agent config from the conversation - const { model, endpoint, promptPrefix, spec, ...rest } = conversation; - - if (!endpoint || !model) { - logger.warn('[loadAddedAgent] Missing required endpoint or model for ephemeral agent'); - return null; - } - - // If both primary and added agents are ephemeral, duplicate tools from primary agent - const primaryIsEphemeral = primaryAgent && isEphemeralAgentId(primaryAgent.id); - if (primaryIsEphemeral && Array.isArray(primaryAgent.tools)) { - // Get endpoint config and model spec for display name fallbacks - const appConfig = req.config; - let endpointConfig = appConfig?.endpoints?.[endpoint]; - if (!isAgentsEndpoint(endpoint) && !endpointConfig) { - try { - endpointConfig = getCustomEndpointConfig({ endpoint, appConfig }); - } catch (err) { - logger.error('[loadAddedAgent] Error getting custom endpoint config', err); - } - } - - // Look up model spec for label fallback - const modelSpecs = appConfig?.modelSpecs?.list; - const modelSpec = spec != null && spec !== '' ? modelSpecs?.find((s) => s.name === spec) : null; - - // For ephemeral agents, use modelLabel if provided, then model spec's label, - // then modelDisplayLabel from endpoint config, otherwise empty string to show model name - const sender = rest.modelLabel ?? modelSpec?.label ?? endpointConfig?.modelDisplayLabel ?? ''; - - const ephemeralId = encodeEphemeralAgentId({ endpoint, model, sender, index: 1 }); - - return { - id: ephemeralId, - instructions: promptPrefix || '', - provider: endpoint, - model_parameters: {}, - model, - tools: [...primaryAgent.tools], - }; - } - - // Extract ephemeral agent options from conversation if present - const ephemeralAgent = rest.ephemeralAgent; - const mcpServers = new Set(ephemeralAgent?.mcp); - const userId = req.user?.id; - - // Check model spec for MCP servers - const modelSpecs = req.config?.modelSpecs?.list; - let modelSpec = null; - if (spec != null && spec !== '') { - modelSpec = modelSpecs?.find((s) => s.name === spec) || null; - } - if (modelSpec?.mcpServers) { - for (const mcpServer of modelSpec.mcpServers) { - mcpServers.add(mcpServer); - } - } - - /** @type {string[]} */ - const tools = []; - if (ephemeralAgent?.execute_code === true || modelSpec?.executeCode === true) { - tools.push(Tools.execute_code); - } - if (ephemeralAgent?.file_search === true || modelSpec?.fileSearch === true) { - tools.push(Tools.file_search); - } - if (ephemeralAgent?.web_search === true || modelSpec?.webSearch === true) { - tools.push(Tools.web_search); - } - - const addedServers = new Set(); - if (mcpServers.size > 0) { - for (const mcpServer of mcpServers) { - if (addedServers.has(mcpServer)) { - continue; - } - const serverTools = await getMCPServerTools(userId, mcpServer); - if (!serverTools) { - tools.push(`${mcp_all}${mcp_delimiter}${mcpServer}`); - addedServers.add(mcpServer); - continue; - } - tools.push(...Object.keys(serverTools)); - addedServers.add(mcpServer); - } - } - - // Build model_parameters from conversation fields - const model_parameters = {}; - const paramKeys = [ - 'temperature', - 'top_p', - 'topP', - 'topK', - 'presence_penalty', - 'frequency_penalty', - 'maxOutputTokens', - 'maxTokens', - 'max_tokens', - ]; - - for (const key of paramKeys) { - if (rest[key] != null) { - model_parameters[key] = rest[key]; - } - } - - // Get endpoint config for modelDisplayLabel fallback - const appConfig = req.config; - let endpointConfig = appConfig?.endpoints?.[endpoint]; - if (!isAgentsEndpoint(endpoint) && !endpointConfig) { - try { - endpointConfig = getCustomEndpointConfig({ endpoint, appConfig }); - } catch (err) { - logger.error('[loadAddedAgent] Error getting custom endpoint config', err); - } - } - - // For ephemeral agents, use modelLabel if provided, then model spec's label, - // then modelDisplayLabel from endpoint config, otherwise empty string to show model name - const sender = rest.modelLabel ?? modelSpec?.label ?? endpointConfig?.modelDisplayLabel ?? ''; - - /** Encoded ephemeral agent ID with endpoint, model, sender, and index=1 to distinguish from primary */ - const ephemeralId = encodeEphemeralAgentId({ endpoint, model, sender, index: 1 }); - - const result = { - id: ephemeralId, - instructions: promptPrefix || '', - provider: endpoint, - model_parameters, - model, - tools, - }; - - if (ephemeralAgent?.artifacts != null && ephemeralAgent.artifacts) { - result.artifacts = ephemeralAgent.artifacts; - } - - return result; -}; - -module.exports = { - ADDED_AGENT_ID, - loadAddedAgent, - setGetAgent, -}; diff --git a/api/models/spendTokens.js b/api/models/spendTokens.js deleted file mode 100644 index afe05969d8..0000000000 --- a/api/models/spendTokens.js +++ /dev/null @@ -1,140 +0,0 @@ -const { logger } = require('@librechat/data-schemas'); -const { createTransaction, createStructuredTransaction } = require('./Transaction'); -/** - * Creates up to two transactions to record the spending of tokens. - * - * @function - * @async - * @param {txData} txData - Transaction data. - * @param {Object} tokenUsage - The number of tokens used. - * @param {Number} tokenUsage.promptTokens - The number of prompt tokens used. - * @param {Number} tokenUsage.completionTokens - The number of completion tokens used. - * @returns {Promise} - Returns nothing. - * @throws {Error} - Throws an error if there's an issue creating the transactions. - */ -const spendTokens = async (txData, tokenUsage) => { - const { promptTokens, completionTokens } = tokenUsage; - logger.debug( - `[spendTokens] conversationId: ${txData.conversationId}${ - txData?.context ? ` | Context: ${txData?.context}` : '' - } | Token usage: `, - { - promptTokens, - completionTokens, - }, - ); - let prompt, completion; - const normalizedPromptTokens = Math.max(promptTokens ?? 0, 0); - try { - if (promptTokens !== undefined) { - prompt = await createTransaction({ - ...txData, - tokenType: 'prompt', - rawAmount: promptTokens === 0 ? 0 : -normalizedPromptTokens, - inputTokenCount: normalizedPromptTokens, - }); - } - - if (completionTokens !== undefined) { - completion = await createTransaction({ - ...txData, - tokenType: 'completion', - rawAmount: completionTokens === 0 ? 0 : -Math.max(completionTokens, 0), - inputTokenCount: normalizedPromptTokens, - }); - } - - if (prompt || completion) { - logger.debug('[spendTokens] Transaction data record against balance:', { - user: txData.user, - prompt: prompt?.prompt, - promptRate: prompt?.rate, - completion: completion?.completion, - completionRate: completion?.rate, - balance: completion?.balance ?? prompt?.balance, - }); - } else { - logger.debug('[spendTokens] No transactions incurred against balance'); - } - } catch (err) { - logger.error('[spendTokens]', err); - } -}; - -/** - * Creates transactions to record the spending of structured tokens. - * - * @function - * @async - * @param {txData} txData - Transaction data. - * @param {Object} tokenUsage - The number of tokens used. - * @param {Object} tokenUsage.promptTokens - The number of prompt tokens used. - * @param {Number} tokenUsage.promptTokens.input - The number of input tokens. - * @param {Number} tokenUsage.promptTokens.write - The number of write tokens. - * @param {Number} tokenUsage.promptTokens.read - The number of read tokens. - * @param {Number} tokenUsage.completionTokens - The number of completion tokens used. - * @returns {Promise} - Returns nothing. - * @throws {Error} - Throws an error if there's an issue creating the transactions. - */ -const spendStructuredTokens = async (txData, tokenUsage) => { - const { promptTokens, completionTokens } = tokenUsage; - logger.debug( - `[spendStructuredTokens] conversationId: ${txData.conversationId}${ - txData?.context ? ` | Context: ${txData?.context}` : '' - } | Token usage: `, - { - promptTokens, - completionTokens, - }, - ); - let prompt, completion; - try { - if (promptTokens) { - const input = Math.max(promptTokens.input ?? 0, 0); - const write = Math.max(promptTokens.write ?? 0, 0); - const read = Math.max(promptTokens.read ?? 0, 0); - const totalInputTokens = input + write + read; - prompt = await createStructuredTransaction({ - ...txData, - tokenType: 'prompt', - inputTokens: -input, - writeTokens: -write, - readTokens: -read, - inputTokenCount: totalInputTokens, - }); - } - - if (completionTokens) { - const totalInputTokens = promptTokens - ? Math.max(promptTokens.input ?? 0, 0) + - Math.max(promptTokens.write ?? 0, 0) + - Math.max(promptTokens.read ?? 0, 0) - : undefined; - completion = await createTransaction({ - ...txData, - tokenType: 'completion', - rawAmount: -Math.max(completionTokens, 0), - inputTokenCount: totalInputTokens, - }); - } - - if (prompt || completion) { - logger.debug('[spendStructuredTokens] Transaction data record against balance:', { - user: txData.user, - prompt: prompt?.prompt, - promptRate: prompt?.rate, - completion: completion?.completion, - completionRate: completion?.rate, - balance: completion?.balance ?? prompt?.balance, - }); - } else { - logger.debug('[spendStructuredTokens] No transactions incurred against balance'); - } - } catch (err) { - logger.error('[spendStructuredTokens]', err); - } - - return { prompt, completion }; -}; - -module.exports = { spendTokens, spendStructuredTokens }; diff --git a/api/models/userMethods.js b/api/models/userMethods.js deleted file mode 100644 index b57b24e641..0000000000 --- a/api/models/userMethods.js +++ /dev/null @@ -1,31 +0,0 @@ -const bcrypt = require('bcryptjs'); - -/** - * Compares the provided password with the user's password. - * - * @param {IUser} user - The user to compare the password for. - * @param {string} candidatePassword - The password to test against the user's password. - * @returns {Promise} A promise that resolves to a boolean indicating if the password matches. - */ -const comparePassword = async (user, candidatePassword) => { - if (!user) { - throw new Error('No user provided'); - } - - if (!user.password) { - throw new Error('No password, likely an email first registered via Social/OIDC login'); - } - - return new Promise((resolve, reject) => { - bcrypt.compare(candidatePassword, user.password, (err, isMatch) => { - if (err) { - reject(err); - } - resolve(isMatch); - }); - }); -}; - -module.exports = { - comparePassword, -}; diff --git a/api/package.json b/api/package.json index 05755c6020..2255679dae 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "@librechat/backend", - "version": "v0.8.2", + "version": "v0.8.4-rc1", "description": "", "scripts": { "start": "echo 'please run this from the root directory'", @@ -44,13 +44,14 @@ "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", "@langchain/core": "^0.3.80", - "@librechat/agents": "^3.1.41", + "@librechat/agents": "^3.1.57", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", - "@modelcontextprotocol/sdk": "^1.26.0", + "@modelcontextprotocol/sdk": "^1.27.1", "@node-saml/passport-saml": "^5.1.0", "@smithy/node-http-handler": "^4.4.5", + "ai-tokenizer": "^1.0.6", "axios": "^1.13.5", "bcryptjs": "^2.4.3", "compression": "^1.8.1", @@ -63,10 +64,10 @@ "eventsource": "^3.0.2", "express": "^5.2.1", "express-mongo-sanitize": "^2.2.0", - "express-rate-limit": "^8.2.1", + "express-rate-limit": "^8.3.0", "express-session": "^1.18.2", "express-static-gzip": "^2.2.0", - "file-type": "^18.7.0", + "file-type": "^21.3.2", "firebase": "^11.0.2", "form-data": "^4.0.4", "handlebars": "^4.7.7", @@ -80,13 +81,14 @@ "klona": "^2.0.6", "librechat-data-provider": "*", "lodash": "^4.17.23", + "mammoth": "^1.11.0", "mathjs": "^15.1.0", "meilisearch": "^0.38.0", "memorystore": "^1.6.7", "mime": "^3.0.0", "module-alias": "^2.2.3", "mongoose": "^8.12.1", - "multer": "^2.0.2", + "multer": "^2.1.1", "nanoid": "^3.3.7", "node-fetch": "^2.7.0", "nodemailer": "^7.0.11", @@ -102,14 +104,15 @@ "passport-jwt": "^4.0.1", "passport-ldapauth": "^3.0.1", "passport-local": "^1.0.0", + "pdfjs-dist": "^5.4.624", "rate-limit-redis": "^4.2.0", "sharp": "^0.33.5", - "tiktoken": "^1.0.15", "traverse": "^0.6.7", "ua-parser-js": "^1.0.36", - "undici": "^7.18.2", + "undici": "^7.24.1", "winston": "^3.11.0", "winston-daily-rotate-file": "^5.0.0", + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", "zod": "^3.22.4" }, "devDependencies": { diff --git a/api/server/cleanup.js b/api/server/cleanup.js index c482a2267e..364c02cd8a 100644 --- a/api/server/cleanup.js +++ b/api/server/cleanup.js @@ -35,7 +35,6 @@ const graphPropsToClean = [ 'tools', 'signal', 'config', - 'agentContexts', 'messages', 'contentData', 'stepKeyIds', @@ -277,7 +276,16 @@ function disposeClient(client) { if (client.run) { if (client.run.Graph) { - client.run.Graph.resetValues(); + if (typeof client.run.Graph.clearHeavyState === 'function') { + client.run.Graph.clearHeavyState(); + } else { + client.run.Graph.resetValues(); + } + + if (client.run.Graph.agentContexts) { + client.run.Graph.agentContexts.clear(); + client.run.Graph.agentContexts = null; + } graphPropsToClean.forEach((prop) => { if (client.run.Graph[prop] !== undefined) { diff --git a/api/server/controllers/AuthController.js b/api/server/controllers/AuthController.js index 22e53dcfc9..eb44feffa4 100644 --- a/api/server/controllers/AuthController.js +++ b/api/server/controllers/AuthController.js @@ -18,8 +18,7 @@ const { findUser, } = require('~/models'); const { getGraphApiToken } = require('~/server/services/GraphTokenService'); -const { getOAuthReconnectionManager } = require('~/config'); -const { getOpenIdConfig } = require('~/strategies'); +const { getOpenIdConfig, getOpenIdEmail } = require('~/strategies'); const registrationController = async (req, res) => { try { @@ -79,11 +78,16 @@ const refreshController = async (req, res) => { try { const openIdConfig = getOpenIdConfig(); - const tokenset = await openIdClient.refreshTokenGrant(openIdConfig, refreshToken); + const refreshParams = process.env.OPENID_SCOPE ? { scope: process.env.OPENID_SCOPE } : {}; + const tokenset = await openIdClient.refreshTokenGrant( + openIdConfig, + refreshToken, + refreshParams, + ); const claims = tokenset.claims(); const { user, error, migration } = await findOpenIDUser({ findUser, - email: claims.email, + email: getOpenIdEmail(claims), openidId: claims.sub, idOnTheSource: claims.oid, strategyName: 'refreshController', @@ -115,14 +119,8 @@ const refreshController = async (req, res) => { const token = setOpenIDAuthTokens(tokenset, req, res, user._id.toString(), refreshToken); - user.federatedTokens = { - access_token: tokenset.access_token, - id_token: tokenset.id_token, - refresh_token: refreshToken, - expires_at: claims.exp, - }; - - return res.status(200).send({ token, user }); + const { password: _pw, __v: _v, totpSecret: _ts, backupCodes: _bc, ...safeUser } = user; + return res.status(200).send({ token, user: safeUser }); } catch (error) { logger.error('[refreshController] OpenID token refresh error', error); return res.status(403).send('Invalid OpenID refresh token'); @@ -161,17 +159,6 @@ const refreshController = async (req, res) => { if (session && session.expiration > new Date()) { const token = await setAuthTokens(userId, res, session); - // trigger OAuth MCP server reconnection asynchronously (best effort) - try { - void getOAuthReconnectionManager() - .reconnectServers(userId) - .catch((err) => { - logger.error('[refreshController] Error reconnecting OAuth MCP servers:', err); - }); - } catch (err) { - logger.warn(`[refreshController] Cannot attempt OAuth MCP servers reconnection:`, err); - } - res.status(200).send({ token, user }); } else if (req?.query?.retry) { // Retrying from a refresh token request that failed (401) @@ -203,15 +190,6 @@ const graphTokenController = async (req, res) => { }); } - // Extract access token from Authorization header - const authHeader = req.headers.authorization; - if (!authHeader || !authHeader.startsWith('Bearer ')) { - return res.status(401).json({ - message: 'Valid authorization token required', - }); - } - - // Get scopes from query parameters const scopes = req.query.scopes; if (!scopes) { return res.status(400).json({ @@ -219,7 +197,13 @@ const graphTokenController = async (req, res) => { }); } - const accessToken = authHeader.substring(7); // Remove 'Bearer ' prefix + const accessToken = req.user.federatedTokens?.access_token; + if (!accessToken) { + return res.status(401).json({ + message: 'No federated access token available for token exchange', + }); + } + const tokenResponse = await getGraphApiToken(req.user, accessToken, scopes); res.json(tokenResponse); diff --git a/api/server/controllers/AuthController.spec.js b/api/server/controllers/AuthController.spec.js new file mode 100644 index 0000000000..964947def9 --- /dev/null +++ b/api/server/controllers/AuthController.spec.js @@ -0,0 +1,318 @@ +jest.mock('@librechat/data-schemas', () => ({ + logger: { error: jest.fn(), debug: jest.fn(), warn: jest.fn(), info: jest.fn() }, +})); +jest.mock('~/server/services/GraphTokenService', () => ({ + getGraphApiToken: jest.fn(), +})); +jest.mock('~/server/services/AuthService', () => ({ + requestPasswordReset: jest.fn(), + setOpenIDAuthTokens: jest.fn(), + resetPassword: jest.fn(), + setAuthTokens: jest.fn(), + registerUser: jest.fn(), +})); +jest.mock('~/strategies', () => ({ getOpenIdConfig: jest.fn(), getOpenIdEmail: jest.fn() })); +jest.mock('openid-client', () => ({ refreshTokenGrant: jest.fn() })); +jest.mock('~/models', () => ({ + deleteAllUserSessions: jest.fn(), + getUserById: jest.fn(), + findSession: jest.fn(), + updateUser: jest.fn(), + findUser: jest.fn(), +})); +jest.mock('@librechat/api', () => ({ + isEnabled: jest.fn(), + findOpenIDUser: jest.fn(), +})); + +const openIdClient = require('openid-client'); +const { isEnabled, findOpenIDUser } = require('@librechat/api'); +const { graphTokenController, refreshController } = require('./AuthController'); +const { getGraphApiToken } = require('~/server/services/GraphTokenService'); +const { setOpenIDAuthTokens } = require('~/server/services/AuthService'); +const { getOpenIdConfig, getOpenIdEmail } = require('~/strategies'); +const { updateUser } = require('~/models'); + +describe('graphTokenController', () => { + let req, res; + + beforeEach(() => { + jest.clearAllMocks(); + isEnabled.mockReturnValue(true); + + req = { + user: { + openidId: 'oid-123', + provider: 'openid', + federatedTokens: { + access_token: 'federated-access-token', + id_token: 'federated-id-token', + }, + }, + headers: { authorization: 'Bearer app-jwt-which-is-id-token' }, + query: { scopes: 'https://graph.microsoft.com/.default' }, + }; + + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + getGraphApiToken.mockResolvedValue({ + access_token: 'graph-access-token', + token_type: 'Bearer', + expires_in: 3600, + }); + }); + + it('should pass federatedTokens.access_token as OBO assertion, not the auth header bearer token', async () => { + await graphTokenController(req, res); + + expect(getGraphApiToken).toHaveBeenCalledWith( + req.user, + 'federated-access-token', + 'https://graph.microsoft.com/.default', + ); + expect(getGraphApiToken).not.toHaveBeenCalledWith( + expect.anything(), + 'app-jwt-which-is-id-token', + expect.anything(), + ); + }); + + it('should return the graph token response on success', async () => { + await graphTokenController(req, res); + + expect(res.json).toHaveBeenCalledWith({ + access_token: 'graph-access-token', + token_type: 'Bearer', + expires_in: 3600, + }); + }); + + it('should return 403 when user is not authenticated via Entra ID', async () => { + req.user.provider = 'google'; + req.user.openidId = undefined; + + await graphTokenController(req, res); + + expect(res.status).toHaveBeenCalledWith(403); + expect(getGraphApiToken).not.toHaveBeenCalled(); + }); + + it('should return 403 when OPENID_REUSE_TOKENS is not enabled', async () => { + isEnabled.mockReturnValue(false); + + await graphTokenController(req, res); + + expect(res.status).toHaveBeenCalledWith(403); + expect(getGraphApiToken).not.toHaveBeenCalled(); + }); + + it('should return 400 when scopes query param is missing', async () => { + req.query.scopes = undefined; + + await graphTokenController(req, res); + + expect(res.status).toHaveBeenCalledWith(400); + expect(getGraphApiToken).not.toHaveBeenCalled(); + }); + + it('should return 401 when federatedTokens.access_token is missing', async () => { + req.user.federatedTokens = {}; + + await graphTokenController(req, res); + + expect(res.status).toHaveBeenCalledWith(401); + expect(getGraphApiToken).not.toHaveBeenCalled(); + }); + + it('should return 401 when federatedTokens is absent entirely', async () => { + req.user.federatedTokens = undefined; + + await graphTokenController(req, res); + + expect(res.status).toHaveBeenCalledWith(401); + expect(getGraphApiToken).not.toHaveBeenCalled(); + }); + + it('should return 500 when getGraphApiToken throws', async () => { + getGraphApiToken.mockRejectedValue(new Error('OBO exchange failed')); + + await graphTokenController(req, res); + + expect(res.status).toHaveBeenCalledWith(500); + expect(res.json).toHaveBeenCalledWith({ + message: 'Failed to obtain Microsoft Graph token', + }); + }); +}); + +describe('refreshController – OpenID path', () => { + const mockTokenset = { + claims: jest.fn(), + access_token: 'new-access', + id_token: 'new-id', + refresh_token: 'new-refresh', + }; + + const baseClaims = { + sub: 'oidc-sub-123', + oid: 'oid-456', + email: 'user@example.com', + exp: 9999999999, + }; + + const defaultUser = { + _id: 'user-db-id', + email: baseClaims.email, + openidId: baseClaims.sub, + password: '$2b$10$hashedpassword', + __v: 0, + totpSecret: 'encrypted-totp-secret', + backupCodes: ['hashed-code-1', 'hashed-code-2'], + }; + + let req, res; + + beforeEach(() => { + jest.clearAllMocks(); + + isEnabled.mockReturnValue(true); + getOpenIdConfig.mockReturnValue({ some: 'config' }); + openIdClient.refreshTokenGrant.mockResolvedValue(mockTokenset); + mockTokenset.claims.mockReturnValue(baseClaims); + getOpenIdEmail.mockReturnValue(baseClaims.email); + setOpenIDAuthTokens.mockReturnValue('new-app-token'); + findOpenIDUser.mockResolvedValue({ user: { ...defaultUser }, error: null, migration: false }); + updateUser.mockResolvedValue({}); + + req = { + headers: { cookie: 'token_provider=openid; refreshToken=stored-refresh' }, + session: {}, + }; + + res = { + status: jest.fn().mockReturnThis(), + send: jest.fn().mockReturnThis(), + redirect: jest.fn(), + }; + }); + + it('should call getOpenIdEmail with token claims and use result for findOpenIDUser', async () => { + await refreshController(req, res); + + expect(getOpenIdEmail).toHaveBeenCalledWith(baseClaims); + expect(findOpenIDUser).toHaveBeenCalledWith( + expect.objectContaining({ email: baseClaims.email }), + ); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('should use OPENID_EMAIL_CLAIM-resolved value when claim is present in token', async () => { + const claimsWithUpn = { ...baseClaims, upn: 'user@corp.example.com' }; + mockTokenset.claims.mockReturnValue(claimsWithUpn); + getOpenIdEmail.mockReturnValue('user@corp.example.com'); + + const user = { + _id: 'user-db-id', + email: 'user@corp.example.com', + openidId: baseClaims.sub, + }; + findOpenIDUser.mockResolvedValue({ user, error: null, migration: false }); + + await refreshController(req, res); + + expect(getOpenIdEmail).toHaveBeenCalledWith(claimsWithUpn); + expect(findOpenIDUser).toHaveBeenCalledWith( + expect.objectContaining({ email: 'user@corp.example.com' }), + ); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('should fall back to claims.email when configured claim is absent from token claims', async () => { + getOpenIdEmail.mockReturnValue(baseClaims.email); + + await refreshController(req, res); + + expect(findOpenIDUser).toHaveBeenCalledWith( + expect.objectContaining({ email: baseClaims.email }), + ); + }); + + it('should not expose sensitive fields or federatedTokens in refresh response', async () => { + await refreshController(req, res); + + const sentPayload = res.send.mock.calls[0][0]; + expect(sentPayload).toEqual({ + token: 'new-app-token', + user: expect.objectContaining({ + _id: 'user-db-id', + email: baseClaims.email, + openidId: baseClaims.sub, + }), + }); + expect(sentPayload.user).not.toHaveProperty('federatedTokens'); + expect(sentPayload.user).not.toHaveProperty('password'); + expect(sentPayload.user).not.toHaveProperty('totpSecret'); + expect(sentPayload.user).not.toHaveProperty('backupCodes'); + expect(sentPayload.user).not.toHaveProperty('__v'); + }); + + it('should update openidId when migration is triggered on refresh', async () => { + const user = { _id: 'user-db-id', email: baseClaims.email, openidId: null }; + findOpenIDUser.mockResolvedValue({ user, error: null, migration: true }); + + await refreshController(req, res); + + expect(updateUser).toHaveBeenCalledWith( + 'user-db-id', + expect.objectContaining({ provider: 'openid', openidId: baseClaims.sub }), + ); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('should return 401 and redirect to /login when findOpenIDUser returns no user', async () => { + findOpenIDUser.mockResolvedValue({ user: null, error: null, migration: false }); + + await refreshController(req, res); + + expect(res.status).toHaveBeenCalledWith(401); + expect(res.redirect).toHaveBeenCalledWith('/login'); + }); + + it('should return 401 and redirect when findOpenIDUser returns an error', async () => { + findOpenIDUser.mockResolvedValue({ user: null, error: 'AUTH_FAILED', migration: false }); + + await refreshController(req, res); + + expect(res.status).toHaveBeenCalledWith(401); + expect(res.redirect).toHaveBeenCalledWith('/login'); + }); + + it('should skip OpenID path when token_provider is not openid', async () => { + req.headers.cookie = 'token_provider=local; refreshToken=some-token'; + + await refreshController(req, res); + + expect(openIdClient.refreshTokenGrant).not.toHaveBeenCalled(); + }); + + it('should skip OpenID path when OPENID_REUSE_TOKENS is disabled', async () => { + isEnabled.mockReturnValue(false); + + await refreshController(req, res); + + expect(openIdClient.refreshTokenGrant).not.toHaveBeenCalled(); + }); + + it('should return 200 with token not provided when refresh token is absent', async () => { + req.headers.cookie = 'token_provider=openid'; + req.session = {}; + + await refreshController(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith('Refresh token not provided'); + }); +}); diff --git a/api/server/controllers/Balance.js b/api/server/controllers/Balance.js index c892a73b0c..fd9b32e74c 100644 --- a/api/server/controllers/Balance.js +++ b/api/server/controllers/Balance.js @@ -1,24 +1,22 @@ -const { Balance } = require('~/db/models'); +const { findBalanceByUser } = require('~/models'); async function balanceController(req, res) { - const balanceData = await Balance.findOne( - { user: req.user.id }, - '-_id tokenCredits autoRefillEnabled refillIntervalValue refillIntervalUnit lastRefill refillAmount', - ).lean(); + const balanceData = await findBalanceByUser(req.user.id); if (!balanceData) { return res.status(404).json({ error: 'Balance not found' }); } - // If auto-refill is not enabled, remove auto-refill related fields from the response - if (!balanceData.autoRefillEnabled) { - delete balanceData.refillIntervalValue; - delete balanceData.refillIntervalUnit; - delete balanceData.lastRefill; - delete balanceData.refillAmount; + const { _id: _, ...result } = balanceData; + + if (!result.autoRefillEnabled) { + delete result.refillIntervalValue; + delete result.refillIntervalUnit; + delete result.lastRefill; + delete result.refillAmount; } - res.status(200).json(balanceData); + res.status(200).json(result); } module.exports = balanceController; diff --git a/api/server/controllers/PermissionsController.js b/api/server/controllers/PermissionsController.js index 51993d083c..1b409ac931 100644 --- a/api/server/controllers/PermissionsController.js +++ b/api/server/controllers/PermissionsController.js @@ -9,22 +9,24 @@ const { enrichRemoteAgentPrincipals, backfillRemoteAgentPermissions } = require( const { bulkUpdateResourcePermissions, ensureGroupPrincipalExists, + getResourcePermissionsMap, + findAccessibleResources, getEffectivePermissions, ensurePrincipalExists, getAvailableRoles, - findAccessibleResources, - getResourcePermissionsMap, } = require('~/server/services/PermissionService'); const { searchPrincipals: searchLocalPrincipals, sortPrincipalsByRelevance, calculateRelevanceScore, + findRoleByIdentifier, + aggregateAclEntries, + bulkWriteAclEntries, } = require('~/models'); const { entraIdPrincipalFeatureEnabled, searchEntraIdPrincipals, } = require('~/server/services/GraphApiService'); -const { AclEntry, AccessRole } = require('~/db/models'); /** * Generic controller for resource permission endpoints @@ -185,8 +187,7 @@ const getResourcePermissions = async (req, res) => { const { resourceType, resourceId } = req.params; validateResourceType(resourceType); - // Use aggregation pipeline for efficient single-query data retrieval - const results = await AclEntry.aggregate([ + const results = await aggregateAclEntries([ // Match ACL entries for this resource { $match: { @@ -282,7 +283,12 @@ const getResourcePermissions = async (req, res) => { } if (resourceType === ResourceType.REMOTE_AGENT) { - const enricherDeps = { AclEntry, AccessRole, logger }; + const enricherDeps = { + aggregateAclEntries, + bulkWriteAclEntries, + findRoleByIdentifier, + logger, + }; const enrichResult = await enrichRemoteAgentPrincipals(enricherDeps, resourceId, principals); principals = enrichResult.principals; backfillRemoteAgentPermissions(enricherDeps, resourceId, enrichResult.entriesToBackfill); diff --git a/api/server/controllers/TwoFactorController.js b/api/server/controllers/TwoFactorController.js index fde5965261..18a0ee3f5a 100644 --- a/api/server/controllers/TwoFactorController.js +++ b/api/server/controllers/TwoFactorController.js @@ -1,5 +1,6 @@ const { encryptV3, logger } = require('@librechat/data-schemas'); const { + verifyOTPOrBackupCode, generateBackupCodes, generateTOTPSecret, verifyBackupCode, @@ -13,24 +14,42 @@ const safeAppTitle = (process.env.APP_TITLE || 'LibreChat').replace(/\s+/g, ''); /** * Enable 2FA for the user by generating a new TOTP secret and backup codes. * The secret is encrypted and stored, and 2FA is marked as disabled until confirmed. + * If 2FA is already enabled, requires OTP or backup code verification to re-enroll. */ const enable2FA = async (req, res) => { try { const userId = req.user.id; + const existingUser = await getUserById( + userId, + '+totpSecret +backupCodes _id twoFactorEnabled email', + ); + + if (existingUser && existingUser.twoFactorEnabled) { + const { token, backupCode } = req.body; + const result = await verifyOTPOrBackupCode({ + user: existingUser, + token, + backupCode, + persistBackupUse: false, + }); + + if (!result.verified) { + const msg = result.message ?? 'TOTP token or backup code is required to re-enroll 2FA'; + return res.status(result.status ?? 400).json({ message: msg }); + } + } + const secret = generateTOTPSecret(); const { plainCodes, codeObjects } = await generateBackupCodes(); - - // Encrypt the secret with v3 encryption before saving. const encryptedSecret = encryptV3(secret); - // Update the user record: store the secret & backup codes and set twoFactorEnabled to false. const user = await updateUser(userId, { - totpSecret: encryptedSecret, - backupCodes: codeObjects, - twoFactorEnabled: false, + pendingTotpSecret: encryptedSecret, + pendingBackupCodes: codeObjects, }); - const otpauthUrl = `otpauth://totp/${safeAppTitle}:${user.email}?secret=${secret}&issuer=${safeAppTitle}`; + const email = user.email || (existingUser && existingUser.email) || ''; + const otpauthUrl = `otpauth://totp/${safeAppTitle}:${email}?secret=${secret}&issuer=${safeAppTitle}`; return res.status(200).json({ otpauthUrl, backupCodes: plainCodes }); } catch (err) { @@ -46,13 +65,14 @@ const verify2FA = async (req, res) => { try { const userId = req.user.id; const { token, backupCode } = req.body; - const user = await getUserById(userId, '_id totpSecret backupCodes'); + const user = await getUserById(userId, '+totpSecret +pendingTotpSecret +backupCodes _id'); + const secretSource = user?.pendingTotpSecret ?? user?.totpSecret; - if (!user || !user.totpSecret) { + if (!user || !secretSource) { return res.status(400).json({ message: '2FA not initiated' }); } - const secret = await getTOTPSecret(user.totpSecret); + const secret = await getTOTPSecret(secretSource); let isVerified = false; if (token) { @@ -78,15 +98,28 @@ const confirm2FA = async (req, res) => { try { const userId = req.user.id; const { token } = req.body; - const user = await getUserById(userId, '_id totpSecret'); + const user = await getUserById( + userId, + '+totpSecret +pendingTotpSecret +pendingBackupCodes _id', + ); + const secretSource = user?.pendingTotpSecret ?? user?.totpSecret; - if (!user || !user.totpSecret) { + if (!user || !secretSource) { return res.status(400).json({ message: '2FA not initiated' }); } - const secret = await getTOTPSecret(user.totpSecret); + const secret = await getTOTPSecret(secretSource); if (await verifyTOTP(secret, token)) { - await updateUser(userId, { twoFactorEnabled: true }); + const update = { + totpSecret: user.pendingTotpSecret ?? user.totpSecret, + twoFactorEnabled: true, + pendingTotpSecret: null, + pendingBackupCodes: [], + }; + if (user.pendingBackupCodes?.length) { + update.backupCodes = user.pendingBackupCodes; + } + await updateUser(userId, update); return res.status(200).json(); } return res.status(400).json({ message: 'Invalid token.' }); @@ -104,31 +137,27 @@ const disable2FA = async (req, res) => { try { const userId = req.user.id; const { token, backupCode } = req.body; - const user = await getUserById(userId, '_id totpSecret backupCodes'); + const user = await getUserById(userId, '+totpSecret +backupCodes _id twoFactorEnabled'); if (!user || !user.totpSecret) { return res.status(400).json({ message: '2FA is not setup for this user' }); } if (user.twoFactorEnabled) { - const secret = await getTOTPSecret(user.totpSecret); - let isVerified = false; + const result = await verifyOTPOrBackupCode({ user, token, backupCode }); - if (token) { - isVerified = await verifyTOTP(secret, token); - } else if (backupCode) { - isVerified = await verifyBackupCode({ user, backupCode }); - } else { - return res - .status(400) - .json({ message: 'Either token or backup code is required to disable 2FA' }); - } - - if (!isVerified) { - return res.status(401).json({ message: 'Invalid token or backup code' }); + if (!result.verified) { + const msg = result.message ?? 'Either token or backup code is required to disable 2FA'; + return res.status(result.status ?? 400).json({ message: msg }); } } - await updateUser(userId, { totpSecret: null, backupCodes: [], twoFactorEnabled: false }); + await updateUser(userId, { + totpSecret: null, + backupCodes: [], + twoFactorEnabled: false, + pendingTotpSecret: null, + pendingBackupCodes: [], + }); return res.status(200).json(); } catch (err) { logger.error('[disable2FA]', err); @@ -138,10 +167,28 @@ const disable2FA = async (req, res) => { /** * Regenerate backup codes for the user. + * Requires OTP or backup code verification if 2FA is already enabled. */ const regenerateBackupCodes = async (req, res) => { try { const userId = req.user.id; + const user = await getUserById(userId, '+totpSecret +backupCodes _id twoFactorEnabled'); + + if (!user) { + return res.status(404).json({ message: 'User not found' }); + } + + if (user.twoFactorEnabled) { + const { token, backupCode } = req.body; + const result = await verifyOTPOrBackupCode({ user, token, backupCode }); + + if (!result.verified) { + const msg = + result.message ?? 'TOTP token or backup code is required to regenerate backup codes'; + return res.status(result.status ?? 400).json({ message: msg }); + } + } + const { plainCodes, codeObjects } = await generateBackupCodes(); await updateUser(userId, { backupCodes: codeObjects }); return res.status(200).json({ diff --git a/api/server/controllers/UserController.js b/api/server/controllers/UserController.js index 7a9dd8125e..34be6192ed 100644 --- a/api/server/controllers/UserController.js +++ b/api/server/controllers/UserController.js @@ -1,49 +1,22 @@ const { logger, webSearchKeys } = require('@librechat/data-schemas'); const { Tools, CacheKeys, Constants, FileSources } = require('librechat-data-provider'); const { + getNewS3URL, + needsRefresh, MCPOAuthHandler, MCPTokenStorage, normalizeHttpError, extractWebSearchEnvVars, } = require('@librechat/api'); -const { - deleteAllUserSessions, - deleteAllSharedLinks, - updateUserPlugins, - deleteUserById, - deleteMessages, - deletePresets, - deleteUserKey, - deleteConvos, - deleteFiles, - updateUser, - findToken, - getFiles, -} = require('~/models'); -const { - ConversationTag, - AgentApiKey, - Transaction, - MemoryEntry, - Assistant, - AclEntry, - Balance, - Action, - Group, - Token, - User, -} = require('~/db/models'); const { updateUserPluginAuth, deleteUserPluginAuth } = require('~/server/services/PluginService'); +const { verifyOTPOrBackupCode } = require('~/server/services/twoFactorService'); const { verifyEmail, resendVerificationEmail } = require('~/server/services/AuthService'); const { getMCPManager, getFlowStateManager, getMCPServersRegistry } = require('~/config'); const { invalidateCachedTools } = require('~/server/services/Config/getCachedTools'); -const { needsRefresh, getNewS3URL } = require('~/server/services/Files/S3/crud'); const { processDeleteRequest } = require('~/server/services/Files/process'); const { getAppConfig } = require('~/server/services/Config'); -const { deleteToolCalls } = require('~/models/ToolCall'); -const { deleteUserPrompts } = require('~/models/Prompt'); -const { deleteUserAgents } = require('~/models/Agent'); const { getLogStores } = require('~/cache'); +const db = require('~/models'); const getUserController = async (req, res) => { const appConfig = await getAppConfig({ role: req.user?.role }); @@ -64,7 +37,7 @@ const getUserController = async (req, res) => { const originalAvatar = userData.avatar; try { userData.avatar = await getNewS3URL(userData.avatar); - await updateUser(userData.id, { avatar: userData.avatar }); + await db.updateUser(userData.id, { avatar: userData.avatar }); } catch (error) { userData.avatar = originalAvatar; logger.error('Error getting new S3 URL for avatar:', error); @@ -75,7 +48,7 @@ const getUserController = async (req, res) => { const getTermsStatusController = async (req, res) => { try { - const user = await User.findById(req.user.id); + const user = await db.getUserById(req.user.id, 'termsAccepted'); if (!user) { return res.status(404).json({ message: 'User not found' }); } @@ -88,7 +61,7 @@ const getTermsStatusController = async (req, res) => { const acceptTermsController = async (req, res) => { try { - const user = await User.findByIdAndUpdate(req.user.id, { termsAccepted: true }, { new: true }); + const user = await db.updateUser(req.user.id, { termsAccepted: true }); if (!user) { return res.status(404).json({ message: 'User not found' }); } @@ -101,7 +74,7 @@ const acceptTermsController = async (req, res) => { const deleteUserFiles = async (req) => { try { - const userFiles = await getFiles({ user: req.user.id }); + const userFiles = await db.getFiles({ user: req.user.id }); await processDeleteRequest({ req, files: userFiles, @@ -117,7 +90,7 @@ const updateUserPluginsController = async (req, res) => { const { pluginKey, action, auth, isEntityTool } = req.body; try { if (!isEntityTool) { - await updateUserPlugins(user._id, user.plugins, pluginKey, action); + await db.updateUserPlugins(user._id, user.plugins, pluginKey, action); } if (auth == null) { @@ -241,37 +214,49 @@ const deleteUserController = async (req, res) => { const { user } = req; try { - await deleteMessages({ user: user.id }); // delete user messages - await deleteAllUserSessions({ userId: user.id }); // delete user sessions - await Transaction.deleteMany({ user: user.id }); // delete user transactions - await deleteUserKey({ userId: user.id, all: true }); // delete user keys - await Balance.deleteMany({ user: user._id }); // delete user balances - await deletePresets(user.id); // delete user presets + const existingUser = await db.getUserById( + user.id, + '+totpSecret +backupCodes _id twoFactorEnabled', + ); + if (existingUser && existingUser.twoFactorEnabled) { + const { token, backupCode } = req.body; + const result = await verifyOTPOrBackupCode({ user: existingUser, token, backupCode }); + + if (!result.verified) { + const msg = + result.message ?? + 'TOTP token or backup code is required to delete account with 2FA enabled'; + return res.status(result.status ?? 400).json({ message: msg }); + } + } + + await db.deleteMessages({ user: user.id }); + await db.deleteAllUserSessions({ userId: user.id }); + await db.deleteTransactions({ user: user.id }); + await db.deleteUserKey({ userId: user.id, all: true }); + await db.deleteBalances({ user: user._id }); + await db.deletePresets(user.id); try { - await deleteConvos(user.id); // delete user convos + await db.deleteConvos(user.id); } catch (error) { logger.error('[deleteUserController] Error deleting user convos, likely no convos', error); } - await deleteUserPluginAuth(user.id, null, true); // delete user plugin auth - await deleteUserById(user.id); // delete user - await deleteAllSharedLinks(user.id); // delete user shared links - await deleteUserFiles(req); // delete user files - await deleteFiles(null, user.id); // delete database files in case of orphaned files from previous steps - await deleteToolCalls(user.id); // delete user tool calls - await deleteUserAgents(user.id); // delete user agents - await AgentApiKey.deleteMany({ user: user._id }); // delete user agent API keys - await Assistant.deleteMany({ user: user.id }); // delete user assistants - await ConversationTag.deleteMany({ user: user.id }); // delete user conversation tags - await MemoryEntry.deleteMany({ userId: user.id }); // delete user memory entries - await deleteUserPrompts(req, user.id); // delete user prompts - await Action.deleteMany({ user: user.id }); // delete user actions - await Token.deleteMany({ userId: user.id }); // delete user OAuth tokens - await Group.updateMany( - // remove user from all groups - { memberIds: user.id }, - { $pull: { memberIds: user.id } }, - ); - await AclEntry.deleteMany({ principalId: user._id }); // delete user ACL entries + await deleteUserPluginAuth(user.id, null, true); + await db.deleteUserById(user.id); + await db.deleteAllSharedLinks(user.id); + await deleteUserFiles(req); + await db.deleteFiles(null, user.id); + await db.deleteToolCalls(user.id); + await db.deleteUserAgents(user.id); + await db.deleteAllAgentApiKeys(user._id); + await db.deleteAssistants({ user: user.id }); + await db.deleteConversationTags({ user: user.id }); + await db.deleteAllUserMemories(user.id); + await db.deleteUserPrompts(user.id); + await db.deleteActions({ user: user.id }); + await db.deleteTokens({ userId: user.id }); + await db.removeUserFromAllGroups(user.id); + await db.deleteAclEntries({ principalId: user._id }); logger.info(`User deleted account. Email: ${user.email} ID: ${user.id}`); res.status(200).send({ message: 'User deleted' }); } catch (err) { @@ -331,7 +316,7 @@ const maybeUninstallOAuthMCP = async (userId, pluginKey, appConfig) => { const clientTokenData = await MCPTokenStorage.getClientInfoAndMetadata({ userId, serverName, - findToken, + findToken: db.findToken, }); if (clientTokenData == null) { return; @@ -342,7 +327,7 @@ const maybeUninstallOAuthMCP = async (userId, pluginKey, appConfig) => { const tokens = await MCPTokenStorage.getTokens({ userId, serverName, - findToken, + findToken: db.findToken, }); // 3. revoke OAuth tokens at the provider @@ -352,6 +337,7 @@ const maybeUninstallOAuthMCP = async (userId, pluginKey, appConfig) => { serverConfig.oauth?.revocation_endpoint_auth_methods_supported ?? clientMetadata.revocation_endpoint_auth_methods_supported; const oauthHeaders = serverConfig.oauth_headers ?? {}; + const allowedDomains = getMCPServersRegistry().getAllowedDomains(); if (tokens?.access_token) { try { @@ -367,6 +353,7 @@ const maybeUninstallOAuthMCP = async (userId, pluginKey, appConfig) => { revocationEndpointAuthMethodsSupported, }, oauthHeaders, + allowedDomains, ); } catch (error) { logger.error(`Error revoking OAuth access token for ${serverName}:`, error); @@ -387,6 +374,7 @@ const maybeUninstallOAuthMCP = async (userId, pluginKey, appConfig) => { revocationEndpointAuthMethodsSupported, }, oauthHeaders, + allowedDomains, ); } catch (error) { logger.error(`Error revoking OAuth refresh token for ${serverName}:`, error); @@ -398,7 +386,7 @@ const maybeUninstallOAuthMCP = async (userId, pluginKey, appConfig) => { userId, serverName, deleteToken: async (filter) => { - await Token.deleteOne(filter); + await db.deleteTokens(filter); }, }); diff --git a/api/server/controllers/UserController.spec.js b/api/server/controllers/UserController.spec.js new file mode 100644 index 0000000000..4a96072062 --- /dev/null +++ b/api/server/controllers/UserController.spec.js @@ -0,0 +1,225 @@ +const mongoose = require('mongoose'); +const { MongoMemoryServer } = require('mongodb-memory-server'); + +jest.mock('@librechat/data-schemas', () => { + const actual = jest.requireActual('@librechat/data-schemas'); + return { + ...actual, + logger: { + debug: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + info: jest.fn(), + }, + }; +}); + +jest.mock('~/models', () => { + const _mongoose = require('mongoose'); + return { + deleteAllUserSessions: jest.fn().mockResolvedValue(undefined), + deleteAllSharedLinks: jest.fn().mockResolvedValue(undefined), + deleteAllAgentApiKeys: jest.fn().mockResolvedValue(undefined), + deleteConversationTags: jest.fn().mockResolvedValue(undefined), + deleteAllUserMemories: jest.fn().mockResolvedValue(undefined), + deleteTransactions: jest.fn().mockResolvedValue(undefined), + deleteAclEntries: jest.fn().mockResolvedValue(undefined), + updateUserPlugins: jest.fn(), + deleteAssistants: jest.fn().mockResolvedValue(undefined), + deleteUserById: jest.fn().mockResolvedValue(undefined), + deleteUserPrompts: jest.fn().mockResolvedValue(undefined), + deleteMessages: jest.fn().mockResolvedValue(undefined), + deleteBalances: jest.fn().mockResolvedValue(undefined), + deleteActions: jest.fn().mockResolvedValue(undefined), + deletePresets: jest.fn().mockResolvedValue(undefined), + deleteUserKey: jest.fn().mockResolvedValue(undefined), + deleteToolCalls: jest.fn().mockResolvedValue(undefined), + deleteUserAgents: jest.fn().mockResolvedValue(undefined), + deleteTokens: jest.fn().mockResolvedValue(undefined), + deleteConvos: jest.fn().mockResolvedValue(undefined), + deleteFiles: jest.fn().mockResolvedValue(undefined), + updateUser: jest.fn(), + getUserById: jest.fn().mockResolvedValue(null), + findToken: jest.fn(), + getFiles: jest.fn().mockResolvedValue([]), + removeUserFromAllGroups: jest.fn().mockImplementation(async (userId) => { + const Group = _mongoose.models.Group; + await Group.updateMany({ memberIds: userId }, { $pullAll: { memberIds: [userId] } }); + }), + }; +}); + +jest.mock('~/server/services/PluginService', () => ({ + updateUserPluginAuth: jest.fn(), + deleteUserPluginAuth: jest.fn().mockResolvedValue(undefined), +})); + +jest.mock('~/server/services/AuthService', () => ({ + verifyEmail: jest.fn(), + resendVerificationEmail: jest.fn(), +})); + +jest.mock('sharp', () => + jest.fn(() => ({ + metadata: jest.fn().mockResolvedValue({}), + toFormat: jest.fn().mockReturnThis(), + toBuffer: jest.fn().mockResolvedValue(Buffer.alloc(0)), + })), +); + +jest.mock('@librechat/api', () => ({ + ...jest.requireActual('@librechat/api'), + needsRefresh: jest.fn(), + getNewS3URL: jest.fn(), +})); + +jest.mock('~/server/services/Files/process', () => ({ + processDeleteRequest: jest.fn().mockResolvedValue(undefined), +})); + +jest.mock('~/server/services/Config', () => ({ + getAppConfig: jest.fn().mockResolvedValue({}), + getMCPManager: jest.fn(), + getFlowStateManager: jest.fn(), + getMCPServersRegistry: jest.fn(), +})); + +jest.mock('~/cache', () => ({ + getLogStores: jest.fn(), +})); + +let mongoServer; + +beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + await mongoose.connect(mongoServer.getUri()); +}); + +afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); +}); + +afterEach(async () => { + const collections = mongoose.connection.collections; + for (const key in collections) { + await collections[key].deleteMany({}); + } +}); + +const { deleteUserController } = require('./UserController'); +const { Group } = require('~/db/models'); +const { deleteConvos } = require('~/models'); + +describe('deleteUserController', () => { + const mockRes = { + status: jest.fn().mockReturnThis(), + send: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return 200 on successful deletion', async () => { + const userId = new mongoose.Types.ObjectId(); + const req = { user: { id: userId.toString(), _id: userId, email: 'test@test.com' } }; + + await deleteUserController(req, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(200); + expect(mockRes.send).toHaveBeenCalledWith({ message: 'User deleted' }); + }); + + it('should remove the user from all groups via $pullAll', async () => { + const userId = new mongoose.Types.ObjectId(); + const userIdStr = userId.toString(); + const otherUser = new mongoose.Types.ObjectId().toString(); + + await Group.create([ + { name: 'Group A', memberIds: [userIdStr, otherUser], source: 'local' }, + { name: 'Group B', memberIds: [userIdStr], source: 'local' }, + { name: 'Group C', memberIds: [otherUser], source: 'local' }, + ]); + + const req = { user: { id: userIdStr, _id: userId, email: 'del@test.com' } }; + await deleteUserController(req, mockRes); + + const groups = await Group.find({}).sort({ name: 1 }).lean(); + expect(groups[0].memberIds).toEqual([otherUser]); + expect(groups[1].memberIds).toEqual([]); + expect(groups[2].memberIds).toEqual([otherUser]); + }); + + it('should handle user that exists in no groups', async () => { + const userId = new mongoose.Types.ObjectId(); + await Group.create({ name: 'Empty', memberIds: ['someone-else'], source: 'local' }); + + const req = { user: { id: userId.toString(), _id: userId, email: 'no-groups@test.com' } }; + await deleteUserController(req, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(200); + const group = await Group.findOne({ name: 'Empty' }).lean(); + expect(group.memberIds).toEqual(['someone-else']); + }); + + it('should remove duplicate memberIds if the user appears more than once', async () => { + const userId = new mongoose.Types.ObjectId(); + const userIdStr = userId.toString(); + + await Group.create({ + name: 'Dupes', + memberIds: [userIdStr, 'other', userIdStr], + source: 'local', + }); + + const req = { user: { id: userIdStr, _id: userId, email: 'dupe@test.com' } }; + await deleteUserController(req, mockRes); + + const group = await Group.findOne({ name: 'Dupes' }).lean(); + expect(group.memberIds).toEqual(['other']); + }); + + it('should still succeed when deleteConvos throws', async () => { + const userId = new mongoose.Types.ObjectId(); + deleteConvos.mockRejectedValueOnce(new Error('no convos')); + + const req = { user: { id: userId.toString(), _id: userId, email: 'convos@test.com' } }; + await deleteUserController(req, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(200); + expect(mockRes.send).toHaveBeenCalledWith({ message: 'User deleted' }); + }); + + it('should return 500 when a critical operation fails', async () => { + const userId = new mongoose.Types.ObjectId(); + const { deleteMessages } = require('~/models'); + deleteMessages.mockRejectedValueOnce(new Error('db down')); + + const req = { user: { id: userId.toString(), _id: userId, email: 'fail@test.com' } }; + await deleteUserController(req, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(500); + expect(mockRes.json).toHaveBeenCalledWith({ message: 'Something went wrong.' }); + }); + + it('should use string user.id (not ObjectId user._id) for memberIds removal', async () => { + const userId = new mongoose.Types.ObjectId(); + const userIdStr = userId.toString(); + const otherUser = 'other-user-id'; + + await Group.create({ + name: 'StringCheck', + memberIds: [userIdStr, otherUser], + source: 'local', + }); + + const req = { user: { id: userIdStr, _id: userId, email: 'stringcheck@test.com' } }; + await deleteUserController(req, mockRes); + + const group = await Group.findOne({ name: 'StringCheck' }).lean(); + expect(group.memberIds).toEqual([otherUser]); + expect(group.memberIds).not.toContain(userIdStr); + }); +}); diff --git a/api/server/controllers/__tests__/TwoFactorController.spec.js b/api/server/controllers/__tests__/TwoFactorController.spec.js new file mode 100644 index 0000000000..62531d94a1 --- /dev/null +++ b/api/server/controllers/__tests__/TwoFactorController.spec.js @@ -0,0 +1,264 @@ +const mockGetUserById = jest.fn(); +const mockUpdateUser = jest.fn(); +const mockVerifyOTPOrBackupCode = jest.fn(); +const mockGenerateTOTPSecret = jest.fn(); +const mockGenerateBackupCodes = jest.fn(); +const mockEncryptV3 = jest.fn(); + +jest.mock('@librechat/data-schemas', () => ({ + encryptV3: (...args) => mockEncryptV3(...args), + logger: { error: jest.fn() }, +})); + +jest.mock('~/server/services/twoFactorService', () => ({ + verifyOTPOrBackupCode: (...args) => mockVerifyOTPOrBackupCode(...args), + generateBackupCodes: (...args) => mockGenerateBackupCodes(...args), + generateTOTPSecret: (...args) => mockGenerateTOTPSecret(...args), + verifyBackupCode: jest.fn(), + getTOTPSecret: jest.fn(), + verifyTOTP: jest.fn(), +})); + +jest.mock('~/models', () => ({ + getUserById: (...args) => mockGetUserById(...args), + updateUser: (...args) => mockUpdateUser(...args), +})); + +const { enable2FA, regenerateBackupCodes } = require('~/server/controllers/TwoFactorController'); + +function createRes() { + const res = {}; + res.status = jest.fn().mockReturnValue(res); + res.json = jest.fn().mockReturnValue(res); + return res; +} + +const PLAIN_CODES = ['code1', 'code2', 'code3']; +const CODE_OBJECTS = [ + { codeHash: 'h1', used: false, usedAt: null }, + { codeHash: 'h2', used: false, usedAt: null }, + { codeHash: 'h3', used: false, usedAt: null }, +]; + +beforeEach(() => { + jest.clearAllMocks(); + mockGenerateTOTPSecret.mockReturnValue('NEWSECRET'); + mockGenerateBackupCodes.mockResolvedValue({ plainCodes: PLAIN_CODES, codeObjects: CODE_OBJECTS }); + mockEncryptV3.mockReturnValue('encrypted-secret'); +}); + +describe('enable2FA', () => { + it('allows first-time setup without token — writes to pending fields', async () => { + const req = { user: { id: 'user1' }, body: {} }; + const res = createRes(); + mockGetUserById.mockResolvedValue({ _id: 'user1', twoFactorEnabled: false, email: 'a@b.com' }); + mockUpdateUser.mockResolvedValue({ email: 'a@b.com' }); + + await enable2FA(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith( + expect.objectContaining({ otpauthUrl: expect.any(String), backupCodes: PLAIN_CODES }), + ); + expect(mockVerifyOTPOrBackupCode).not.toHaveBeenCalled(); + const updateCall = mockUpdateUser.mock.calls[0][1]; + expect(updateCall).toHaveProperty('pendingTotpSecret', 'encrypted-secret'); + expect(updateCall).toHaveProperty('pendingBackupCodes', CODE_OBJECTS); + expect(updateCall).not.toHaveProperty('twoFactorEnabled'); + expect(updateCall).not.toHaveProperty('totpSecret'); + expect(updateCall).not.toHaveProperty('backupCodes'); + }); + + it('re-enrollment writes to pending fields, leaving live 2FA intact', async () => { + const req = { user: { id: 'user1' }, body: { token: '123456' } }; + const res = createRes(); + const existingUser = { + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + email: 'a@b.com', + }; + mockGetUserById.mockResolvedValue(existingUser); + mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true }); + mockUpdateUser.mockResolvedValue({ email: 'a@b.com' }); + + await enable2FA(req, res); + + expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith({ + user: existingUser, + token: '123456', + backupCode: undefined, + persistBackupUse: false, + }); + expect(res.status).toHaveBeenCalledWith(200); + const updateCall = mockUpdateUser.mock.calls[0][1]; + expect(updateCall).toHaveProperty('pendingTotpSecret', 'encrypted-secret'); + expect(updateCall).toHaveProperty('pendingBackupCodes', CODE_OBJECTS); + expect(updateCall).not.toHaveProperty('twoFactorEnabled'); + expect(updateCall).not.toHaveProperty('totpSecret'); + }); + + it('allows re-enrollment with valid backup code (persistBackupUse: false)', async () => { + const req = { user: { id: 'user1' }, body: { backupCode: 'backup123' } }; + const res = createRes(); + const existingUser = { + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + email: 'a@b.com', + }; + mockGetUserById.mockResolvedValue(existingUser); + mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true }); + mockUpdateUser.mockResolvedValue({ email: 'a@b.com' }); + + await enable2FA(req, res); + + expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith( + expect.objectContaining({ persistBackupUse: false }), + ); + expect(res.status).toHaveBeenCalledWith(200); + }); + + it('returns error when no token provided and 2FA is enabled', async () => { + const req = { user: { id: 'user1' }, body: {} }; + const res = createRes(); + mockGetUserById.mockResolvedValue({ + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + }); + mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: false, status: 400 }); + + await enable2FA(req, res); + + expect(res.status).toHaveBeenCalledWith(400); + expect(mockUpdateUser).not.toHaveBeenCalled(); + }); + + it('returns 401 when invalid token provided and 2FA is enabled', async () => { + const req = { user: { id: 'user1' }, body: { token: 'wrong' } }; + const res = createRes(); + mockGetUserById.mockResolvedValue({ + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + }); + mockVerifyOTPOrBackupCode.mockResolvedValue({ + verified: false, + status: 401, + message: 'Invalid token or backup code', + }); + + await enable2FA(req, res); + + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith({ message: 'Invalid token or backup code' }); + expect(mockUpdateUser).not.toHaveBeenCalled(); + }); +}); + +describe('regenerateBackupCodes', () => { + it('returns 404 when user not found', async () => { + const req = { user: { id: 'user1' }, body: {} }; + const res = createRes(); + mockGetUserById.mockResolvedValue(null); + + await regenerateBackupCodes(req, res); + + expect(res.status).toHaveBeenCalledWith(404); + expect(res.json).toHaveBeenCalledWith({ message: 'User not found' }); + }); + + it('requires OTP when 2FA is enabled', async () => { + const req = { user: { id: 'user1' }, body: { token: '123456' } }; + const res = createRes(); + mockGetUserById.mockResolvedValue({ + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + }); + mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true }); + mockUpdateUser.mockResolvedValue({}); + + await regenerateBackupCodes(req, res); + + expect(mockVerifyOTPOrBackupCode).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ + backupCodes: PLAIN_CODES, + backupCodesHash: CODE_OBJECTS, + }); + }); + + it('returns error when no token provided and 2FA is enabled', async () => { + const req = { user: { id: 'user1' }, body: {} }; + const res = createRes(); + mockGetUserById.mockResolvedValue({ + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + }); + mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: false, status: 400 }); + + await regenerateBackupCodes(req, res); + + expect(res.status).toHaveBeenCalledWith(400); + }); + + it('returns 401 when invalid token provided and 2FA is enabled', async () => { + const req = { user: { id: 'user1' }, body: { token: 'wrong' } }; + const res = createRes(); + mockGetUserById.mockResolvedValue({ + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + }); + mockVerifyOTPOrBackupCode.mockResolvedValue({ + verified: false, + status: 401, + message: 'Invalid token or backup code', + }); + + await regenerateBackupCodes(req, res); + + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith({ message: 'Invalid token or backup code' }); + }); + + it('includes backupCodesHash in response', async () => { + const req = { user: { id: 'user1' }, body: { token: '123456' } }; + const res = createRes(); + mockGetUserById.mockResolvedValue({ + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + }); + mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true }); + mockUpdateUser.mockResolvedValue({}); + + await regenerateBackupCodes(req, res); + + const responseBody = res.json.mock.calls[0][0]; + expect(responseBody).toHaveProperty('backupCodesHash', CODE_OBJECTS); + expect(responseBody).toHaveProperty('backupCodes', PLAIN_CODES); + }); + + it('allows regeneration without token when 2FA is not enabled', async () => { + const req = { user: { id: 'user1' }, body: {} }; + const res = createRes(); + mockGetUserById.mockResolvedValue({ + _id: 'user1', + twoFactorEnabled: false, + }); + mockUpdateUser.mockResolvedValue({}); + + await regenerateBackupCodes(req, res); + + expect(mockVerifyOTPOrBackupCode).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ + backupCodes: PLAIN_CODES, + backupCodesHash: CODE_OBJECTS, + }); + }); +}); diff --git a/api/server/controllers/__tests__/deleteUser.spec.js b/api/server/controllers/__tests__/deleteUser.spec.js new file mode 100644 index 0000000000..f1e9ae0c90 --- /dev/null +++ b/api/server/controllers/__tests__/deleteUser.spec.js @@ -0,0 +1,286 @@ +const mockGetUserById = jest.fn(); +const mockDeleteMessages = jest.fn(); +const mockDeleteAllUserSessions = jest.fn(); +const mockDeleteUserById = jest.fn(); +const mockDeleteAllSharedLinks = jest.fn(); +const mockDeletePresets = jest.fn(); +const mockDeleteUserKey = jest.fn(); +const mockDeleteConvos = jest.fn(); +const mockDeleteFiles = jest.fn(); +const mockGetFiles = jest.fn(); +const mockUpdateUserPlugins = jest.fn(); +const mockUpdateUser = jest.fn(); +const mockFindToken = jest.fn(); +const mockVerifyOTPOrBackupCode = jest.fn(); +const mockDeleteUserPluginAuth = jest.fn(); +const mockProcessDeleteRequest = jest.fn(); +const mockDeleteToolCalls = jest.fn(); +const mockDeleteUserAgents = jest.fn(); +const mockDeleteUserPrompts = jest.fn(); + +jest.mock('@librechat/data-schemas', () => ({ + logger: { error: jest.fn(), info: jest.fn() }, + webSearchKeys: [], +})); + +jest.mock('librechat-data-provider', () => ({ + Tools: {}, + CacheKeys: {}, + Constants: { mcp_delimiter: '::', mcp_prefix: 'mcp_' }, + FileSources: {}, +})); + +jest.mock('@librechat/api', () => ({ + MCPOAuthHandler: {}, + MCPTokenStorage: {}, + normalizeHttpError: jest.fn(), + extractWebSearchEnvVars: jest.fn(), + needsRefresh: jest.fn(), + getNewS3URL: jest.fn(), +})); + +jest.mock('~/models', () => ({ + deleteAllUserSessions: (...args) => mockDeleteAllUserSessions(...args), + deleteAllSharedLinks: (...args) => mockDeleteAllSharedLinks(...args), + updateUserPlugins: (...args) => mockUpdateUserPlugins(...args), + deleteUserById: (...args) => mockDeleteUserById(...args), + deleteMessages: (...args) => mockDeleteMessages(...args), + deletePresets: (...args) => mockDeletePresets(...args), + deleteUserKey: (...args) => mockDeleteUserKey(...args), + getUserById: (...args) => mockGetUserById(...args), + deleteConvos: (...args) => mockDeleteConvos(...args), + deleteFiles: (...args) => mockDeleteFiles(...args), + updateUser: (...args) => mockUpdateUser(...args), + findToken: (...args) => mockFindToken(...args), + getFiles: (...args) => mockGetFiles(...args), + deleteToolCalls: (...args) => mockDeleteToolCalls(...args), + deleteUserAgents: (...args) => mockDeleteUserAgents(...args), + deleteUserPrompts: (...args) => mockDeleteUserPrompts(...args), + deleteTransactions: jest.fn(), + deleteBalances: jest.fn(), + deleteAllAgentApiKeys: jest.fn(), + deleteAssistants: jest.fn(), + deleteConversationTags: jest.fn(), + deleteAllUserMemories: jest.fn(), + deleteActions: jest.fn(), + deleteTokens: jest.fn(), + removeUserFromAllGroups: jest.fn(), + deleteAclEntries: jest.fn(), +})); + +jest.mock('~/server/services/PluginService', () => ({ + updateUserPluginAuth: jest.fn(), + deleteUserPluginAuth: (...args) => mockDeleteUserPluginAuth(...args), +})); + +jest.mock('~/server/services/twoFactorService', () => ({ + verifyOTPOrBackupCode: (...args) => mockVerifyOTPOrBackupCode(...args), +})); + +jest.mock('~/server/services/AuthService', () => ({ + verifyEmail: jest.fn(), + resendVerificationEmail: jest.fn(), +})); + +jest.mock('~/config', () => ({ + getMCPManager: jest.fn(), + getFlowStateManager: jest.fn(), + getMCPServersRegistry: jest.fn(), +})); + +jest.mock('~/server/services/Config/getCachedTools', () => ({ + invalidateCachedTools: jest.fn(), +})); + +jest.mock('~/server/services/Files/process', () => ({ + processDeleteRequest: (...args) => mockProcessDeleteRequest(...args), +})); + +jest.mock('~/server/services/Config', () => ({ + getAppConfig: jest.fn(), +})); + +jest.mock('~/cache', () => ({ + getLogStores: jest.fn(), +})); + +const { deleteUserController } = require('~/server/controllers/UserController'); + +function createRes() { + const res = {}; + res.status = jest.fn().mockReturnValue(res); + res.json = jest.fn().mockReturnValue(res); + res.send = jest.fn().mockReturnValue(res); + return res; +} + +function stubDeletionMocks() { + mockDeleteMessages.mockResolvedValue(); + mockDeleteAllUserSessions.mockResolvedValue(); + mockDeleteUserKey.mockResolvedValue(); + mockDeletePresets.mockResolvedValue(); + mockDeleteConvos.mockResolvedValue(); + mockDeleteUserPluginAuth.mockResolvedValue(); + mockDeleteUserById.mockResolvedValue(); + mockDeleteAllSharedLinks.mockResolvedValue(); + mockGetFiles.mockResolvedValue([]); + mockProcessDeleteRequest.mockResolvedValue(); + mockDeleteFiles.mockResolvedValue(); + mockDeleteToolCalls.mockResolvedValue(); + mockDeleteUserAgents.mockResolvedValue(); + mockDeleteUserPrompts.mockResolvedValue(); +} + +beforeEach(() => { + jest.clearAllMocks(); + stubDeletionMocks(); +}); + +describe('deleteUserController - 2FA enforcement', () => { + it('proceeds with deletion when 2FA is not enabled', async () => { + const req = { user: { id: 'user1', _id: 'user1', email: 'a@b.com' }, body: {} }; + const res = createRes(); + mockGetUserById.mockResolvedValue({ _id: 'user1', twoFactorEnabled: false }); + + await deleteUserController(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith({ message: 'User deleted' }); + expect(mockDeleteMessages).toHaveBeenCalled(); + expect(mockVerifyOTPOrBackupCode).not.toHaveBeenCalled(); + }); + + it('proceeds with deletion when user has no 2FA record', async () => { + const req = { user: { id: 'user1', _id: 'user1', email: 'a@b.com' }, body: {} }; + const res = createRes(); + mockGetUserById.mockResolvedValue(null); + + await deleteUserController(req, res); + + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith({ message: 'User deleted' }); + }); + + it('returns error when 2FA is enabled and verification fails with 400', async () => { + const req = { user: { id: 'user1', _id: 'user1' }, body: {} }; + const res = createRes(); + mockGetUserById.mockResolvedValue({ + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + }); + mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: false, status: 400 }); + + await deleteUserController(req, res); + + expect(res.status).toHaveBeenCalledWith(400); + expect(mockDeleteMessages).not.toHaveBeenCalled(); + }); + + it('returns 401 when 2FA is enabled and invalid TOTP token provided', async () => { + const existingUser = { + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + }; + const req = { user: { id: 'user1', _id: 'user1' }, body: { token: 'wrong' } }; + const res = createRes(); + mockGetUserById.mockResolvedValue(existingUser); + mockVerifyOTPOrBackupCode.mockResolvedValue({ + verified: false, + status: 401, + message: 'Invalid token or backup code', + }); + + await deleteUserController(req, res); + + expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith({ + user: existingUser, + token: 'wrong', + backupCode: undefined, + }); + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith({ message: 'Invalid token or backup code' }); + expect(mockDeleteMessages).not.toHaveBeenCalled(); + }); + + it('returns 401 when 2FA is enabled and invalid backup code provided', async () => { + const existingUser = { + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + backupCodes: [], + }; + const req = { user: { id: 'user1', _id: 'user1' }, body: { backupCode: 'bad-code' } }; + const res = createRes(); + mockGetUserById.mockResolvedValue(existingUser); + mockVerifyOTPOrBackupCode.mockResolvedValue({ + verified: false, + status: 401, + message: 'Invalid token or backup code', + }); + + await deleteUserController(req, res); + + expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith({ + user: existingUser, + token: undefined, + backupCode: 'bad-code', + }); + expect(res.status).toHaveBeenCalledWith(401); + expect(mockDeleteMessages).not.toHaveBeenCalled(); + }); + + it('deletes account when valid TOTP token provided with 2FA enabled', async () => { + const existingUser = { + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + }; + const req = { + user: { id: 'user1', _id: 'user1', email: 'a@b.com' }, + body: { token: '123456' }, + }; + const res = createRes(); + mockGetUserById.mockResolvedValue(existingUser); + mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true }); + + await deleteUserController(req, res); + + expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith({ + user: existingUser, + token: '123456', + backupCode: undefined, + }); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith({ message: 'User deleted' }); + expect(mockDeleteMessages).toHaveBeenCalled(); + }); + + it('deletes account when valid backup code provided with 2FA enabled', async () => { + const existingUser = { + _id: 'user1', + twoFactorEnabled: true, + totpSecret: 'enc-secret', + backupCodes: [{ codeHash: 'h1', used: false }], + }; + const req = { + user: { id: 'user1', _id: 'user1', email: 'a@b.com' }, + body: { backupCode: 'valid-code' }, + }; + const res = createRes(); + mockGetUserById.mockResolvedValue(existingUser); + mockVerifyOTPOrBackupCode.mockResolvedValue({ verified: true }); + + await deleteUserController(req, res); + + expect(mockVerifyOTPOrBackupCode).toHaveBeenCalledWith({ + user: existingUser, + token: undefined, + backupCode: 'valid-code', + }); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.send).toHaveBeenCalledWith({ message: 'User deleted' }); + expect(mockDeleteMessages).toHaveBeenCalled(); + }); +}); diff --git a/api/server/controllers/agents/__tests__/callbacks.spec.js b/api/server/controllers/agents/__tests__/callbacks.spec.js index 103f9f3236..8bd711f9c6 100644 --- a/api/server/controllers/agents/__tests__/callbacks.spec.js +++ b/api/server/controllers/agents/__tests__/callbacks.spec.js @@ -20,7 +20,6 @@ jest.mock('@librechat/agents', () => ({ getMessageId: jest.fn(), ToolEndHandler: jest.fn(), handleToolCalls: jest.fn(), - ChatModelStreamHandler: jest.fn(), })); jest.mock('~/server/services/Files/Citations', () => ({ diff --git a/api/server/controllers/agents/__tests__/openai.spec.js b/api/server/controllers/agents/__tests__/openai.spec.js index 03a280b545..deeb2ec51d 100644 --- a/api/server/controllers/agents/__tests__/openai.spec.js +++ b/api/server/controllers/agents/__tests__/openai.spec.js @@ -30,9 +30,6 @@ jest.mock('@librechat/agents', () => ({ messages: [], indexTokenCountMap: {}, }), - ChatModelStreamHandler: jest.fn().mockImplementation(() => ({ - handle: jest.fn(), - })), })); jest.mock('@librechat/api', () => ({ @@ -80,10 +77,8 @@ jest.mock('~/server/services/ToolService', () => ({ loadToolsForExecution: jest.fn().mockResolvedValue([]), })); -jest.mock('~/models/spendTokens', () => ({ - spendTokens: mockSpendTokens, - spendStructuredTokens: mockSpendStructuredTokens, -})); +const mockGetMultiplier = jest.fn().mockReturnValue(1); +const mockGetCacheMultiplier = jest.fn().mockReturnValue(null); jest.mock('~/server/controllers/agents/callbacks', () => ({ createToolEndCallback: jest.fn().mockReturnValue(jest.fn()), @@ -93,20 +88,11 @@ jest.mock('~/server/services/PermissionService', () => ({ findAccessibleResources: jest.fn().mockResolvedValue([]), })); -jest.mock('~/models/Conversation', () => ({ - getConvoFiles: jest.fn().mockResolvedValue([]), -})); - -jest.mock('~/models/Agent', () => ({ - getAgent: jest.fn().mockResolvedValue({ - id: 'agent-123', - provider: 'openAI', - model_parameters: { model: 'gpt-4' }, - }), - getAgents: jest.fn().mockResolvedValue([]), -})); +const mockUpdateBalance = jest.fn().mockResolvedValue({}); +const mockBulkInsertTransactions = jest.fn().mockResolvedValue(undefined); jest.mock('~/models', () => ({ + getAgent: jest.fn().mockResolvedValue({ id: 'agent-123', name: 'Test Agent' }), getFiles: jest.fn(), getUserKey: jest.fn(), getMessages: jest.fn(), @@ -115,6 +101,13 @@ jest.mock('~/models', () => ({ getUserCodeFiles: jest.fn(), getToolFilesByIds: jest.fn(), getCodeGeneratedFiles: jest.fn(), + updateBalance: mockUpdateBalance, + bulkInsertTransactions: mockBulkInsertTransactions, + spendTokens: mockSpendTokens, + spendStructuredTokens: mockSpendStructuredTokens, + getMultiplier: mockGetMultiplier, + getCacheMultiplier: mockGetCacheMultiplier, + getConvoFiles: jest.fn().mockResolvedValue([]), })); describe('OpenAIChatCompletionController', () => { @@ -152,13 +145,92 @@ describe('OpenAIChatCompletionController', () => { }; }); + describe('conversation ownership validation', () => { + it('should skip ownership check when conversation_id is not provided', async () => { + const { getConvo } = require('~/models/Conversation'); + await OpenAIChatCompletionController(req, res); + expect(getConvo).not.toHaveBeenCalled(); + }); + + it('should return 400 when conversation_id is not a string', async () => { + const { validateRequest } = require('@librechat/api'); + validateRequest.mockReturnValueOnce({ + request: { model: 'agent-123', messages: [], stream: false, conversation_id: { $gt: '' } }, + }); + + await OpenAIChatCompletionController(req, res); + expect(res.status).toHaveBeenCalledWith(400); + }); + + it('should return 404 when conversation is not owned by user', async () => { + const { validateRequest } = require('@librechat/api'); + const { getConvo } = require('~/models/Conversation'); + validateRequest.mockReturnValueOnce({ + request: { + model: 'agent-123', + messages: [], + stream: false, + conversation_id: 'convo-abc', + }, + }); + getConvo.mockResolvedValueOnce(null); + + await OpenAIChatCompletionController(req, res); + expect(getConvo).toHaveBeenCalledWith('user-123', 'convo-abc'); + expect(res.status).toHaveBeenCalledWith(404); + }); + + it('should proceed when conversation is owned by user', async () => { + const { validateRequest } = require('@librechat/api'); + const { getConvo } = require('~/models/Conversation'); + validateRequest.mockReturnValueOnce({ + request: { + model: 'agent-123', + messages: [], + stream: false, + conversation_id: 'convo-abc', + }, + }); + getConvo.mockResolvedValueOnce({ conversationId: 'convo-abc', user: 'user-123' }); + + await OpenAIChatCompletionController(req, res); + expect(getConvo).toHaveBeenCalledWith('user-123', 'convo-abc'); + expect(res.status).not.toHaveBeenCalledWith(404); + }); + + it('should return 500 when getConvo throws a DB error', async () => { + const { validateRequest } = require('@librechat/api'); + const { getConvo } = require('~/models/Conversation'); + validateRequest.mockReturnValueOnce({ + request: { + model: 'agent-123', + messages: [], + stream: false, + conversation_id: 'convo-abc', + }, + }); + getConvo.mockRejectedValueOnce(new Error('DB connection failed')); + + await OpenAIChatCompletionController(req, res); + expect(res.status).toHaveBeenCalledWith(500); + }); + }); + describe('token usage recording', () => { it('should call recordCollectedUsage after successful non-streaming completion', async () => { await OpenAIChatCompletionController(req, res); expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1); expect(mockRecordCollectedUsage).toHaveBeenCalledWith( - { spendTokens: mockSpendTokens, spendStructuredTokens: mockSpendStructuredTokens }, + { + spendTokens: mockSpendTokens, + spendStructuredTokens: mockSpendStructuredTokens, + pricing: { getMultiplier: mockGetMultiplier, getCacheMultiplier: mockGetCacheMultiplier }, + bulkWriteOps: { + insertMany: mockBulkInsertTransactions, + updateBalance: mockUpdateBalance, + }, + }, expect.objectContaining({ user: 'user-123', conversationId: expect.any(String), @@ -185,12 +257,18 @@ describe('OpenAIChatCompletionController', () => { ); }); - it('should pass spendTokens and spendStructuredTokens as dependencies', async () => { + it('should pass spendTokens, spendStructuredTokens, pricing, and bulkWriteOps as dependencies', async () => { await OpenAIChatCompletionController(req, res); const [deps] = mockRecordCollectedUsage.mock.calls[0]; expect(deps).toHaveProperty('spendTokens', mockSpendTokens); expect(deps).toHaveProperty('spendStructuredTokens', mockSpendStructuredTokens); + expect(deps).toHaveProperty('pricing'); + expect(deps.pricing).toHaveProperty('getMultiplier', mockGetMultiplier); + expect(deps.pricing).toHaveProperty('getCacheMultiplier', mockGetCacheMultiplier); + expect(deps).toHaveProperty('bulkWriteOps'); + expect(deps.bulkWriteOps).toHaveProperty('insertMany', mockBulkInsertTransactions); + expect(deps.bulkWriteOps).toHaveProperty('updateBalance', mockUpdateBalance); }); it('should include model from primaryConfig in recordCollectedUsage params', async () => { diff --git a/api/server/controllers/agents/__tests__/responses.unit.spec.js b/api/server/controllers/agents/__tests__/responses.unit.spec.js index 25e048f2fa..0a63445f24 100644 --- a/api/server/controllers/agents/__tests__/responses.unit.spec.js +++ b/api/server/controllers/agents/__tests__/responses.unit.spec.js @@ -34,9 +34,6 @@ jest.mock('@librechat/agents', () => ({ messages: [], indexTokenCountMap: {}, }), - ChatModelStreamHandler: jest.fn().mockImplementation(() => ({ - handle: jest.fn(), - })), })); jest.mock('@librechat/api', () => ({ @@ -104,10 +101,8 @@ jest.mock('~/server/services/ToolService', () => ({ loadToolsForExecution: jest.fn().mockResolvedValue([]), })); -jest.mock('~/models/spendTokens', () => ({ - spendTokens: mockSpendTokens, - spendStructuredTokens: mockSpendStructuredTokens, -})); +const mockGetMultiplier = jest.fn().mockReturnValue(1); +const mockGetCacheMultiplier = jest.fn().mockReturnValue(null); jest.mock('~/server/controllers/agents/callbacks', () => ({ createToolEndCallback: jest.fn().mockReturnValue(jest.fn()), @@ -118,23 +113,11 @@ jest.mock('~/server/services/PermissionService', () => ({ findAccessibleResources: jest.fn().mockResolvedValue([]), })); -jest.mock('~/models/Conversation', () => ({ - getConvoFiles: jest.fn().mockResolvedValue([]), - saveConvo: jest.fn().mockResolvedValue({}), - getConvo: jest.fn().mockResolvedValue(null), -})); - -jest.mock('~/models/Agent', () => ({ - getAgent: jest.fn().mockResolvedValue({ - id: 'agent-123', - name: 'Test Agent', - provider: 'anthropic', - model_parameters: { model: 'claude-3' }, - }), - getAgents: jest.fn().mockResolvedValue([]), -})); +const mockUpdateBalance = jest.fn().mockResolvedValue({}); +const mockBulkInsertTransactions = jest.fn().mockResolvedValue(undefined); jest.mock('~/models', () => ({ + getAgent: jest.fn().mockResolvedValue({ id: 'agent-123', name: 'Test Agent' }), getFiles: jest.fn(), getUserKey: jest.fn(), getMessages: jest.fn().mockResolvedValue([]), @@ -144,6 +127,15 @@ jest.mock('~/models', () => ({ getUserCodeFiles: jest.fn(), getToolFilesByIds: jest.fn(), getCodeGeneratedFiles: jest.fn(), + updateBalance: mockUpdateBalance, + bulkInsertTransactions: mockBulkInsertTransactions, + spendTokens: mockSpendTokens, + spendStructuredTokens: mockSpendStructuredTokens, + getMultiplier: mockGetMultiplier, + getCacheMultiplier: mockGetCacheMultiplier, + getConvoFiles: jest.fn().mockResolvedValue([]), + saveConvo: jest.fn().mockResolvedValue({}), + getConvo: jest.fn().mockResolvedValue(null), })); describe('createResponse controller', () => { @@ -181,13 +173,117 @@ describe('createResponse controller', () => { }; }); + describe('conversation ownership validation', () => { + it('should skip ownership check when previous_response_id is not provided', async () => { + const { getConvo } = require('~/models/Conversation'); + await createResponse(req, res); + expect(getConvo).not.toHaveBeenCalled(); + }); + + it('should return 400 when previous_response_id is not a string', async () => { + const { validateResponseRequest, sendResponsesErrorResponse } = require('@librechat/api'); + validateResponseRequest.mockReturnValueOnce({ + request: { + model: 'agent-123', + input: 'Hello', + stream: false, + previous_response_id: { $gt: '' }, + }, + }); + + await createResponse(req, res); + expect(sendResponsesErrorResponse).toHaveBeenCalledWith( + res, + 400, + 'previous_response_id must be a string', + 'invalid_request', + ); + }); + + it('should return 404 when conversation is not owned by user', async () => { + const { validateResponseRequest, sendResponsesErrorResponse } = require('@librechat/api'); + const { getConvo } = require('~/models/Conversation'); + validateResponseRequest.mockReturnValueOnce({ + request: { + model: 'agent-123', + input: 'Hello', + stream: false, + previous_response_id: 'resp_abc', + }, + }); + getConvo.mockResolvedValueOnce(null); + + await createResponse(req, res); + expect(getConvo).toHaveBeenCalledWith('user-123', 'resp_abc'); + expect(sendResponsesErrorResponse).toHaveBeenCalledWith( + res, + 404, + 'Conversation not found', + 'not_found', + ); + }); + + it('should proceed when conversation is owned by user', async () => { + const { validateResponseRequest, sendResponsesErrorResponse } = require('@librechat/api'); + const { getConvo } = require('~/models/Conversation'); + validateResponseRequest.mockReturnValueOnce({ + request: { + model: 'agent-123', + input: 'Hello', + stream: false, + previous_response_id: 'resp_abc', + }, + }); + getConvo.mockResolvedValueOnce({ conversationId: 'resp_abc', user: 'user-123' }); + + await createResponse(req, res); + expect(getConvo).toHaveBeenCalledWith('user-123', 'resp_abc'); + expect(sendResponsesErrorResponse).not.toHaveBeenCalledWith( + res, + 404, + expect.any(String), + expect.any(String), + ); + }); + + it('should return 500 when getConvo throws a DB error', async () => { + const { validateResponseRequest, sendResponsesErrorResponse } = require('@librechat/api'); + const { getConvo } = require('~/models/Conversation'); + validateResponseRequest.mockReturnValueOnce({ + request: { + model: 'agent-123', + input: 'Hello', + stream: false, + previous_response_id: 'resp_abc', + }, + }); + getConvo.mockRejectedValueOnce(new Error('DB connection failed')); + + await createResponse(req, res); + expect(sendResponsesErrorResponse).toHaveBeenCalledWith( + res, + 500, + expect.any(String), + expect.any(String), + ); + }); + }); + describe('token usage recording - non-streaming', () => { it('should call recordCollectedUsage after successful non-streaming completion', async () => { await createResponse(req, res); expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1); expect(mockRecordCollectedUsage).toHaveBeenCalledWith( - { spendTokens: mockSpendTokens, spendStructuredTokens: mockSpendStructuredTokens }, + { + spendTokens: mockSpendTokens, + spendStructuredTokens: mockSpendStructuredTokens, + pricing: { getMultiplier: mockGetMultiplier, getCacheMultiplier: mockGetCacheMultiplier }, + bulkWriteOps: { + insertMany: mockBulkInsertTransactions, + updateBalance: mockUpdateBalance, + }, + }, expect.objectContaining({ user: 'user-123', conversationId: expect.any(String), @@ -212,12 +308,18 @@ describe('createResponse controller', () => { ); }); - it('should pass spendTokens and spendStructuredTokens as dependencies', async () => { + it('should pass spendTokens, spendStructuredTokens, pricing, and bulkWriteOps as dependencies', async () => { await createResponse(req, res); const [deps] = mockRecordCollectedUsage.mock.calls[0]; expect(deps).toHaveProperty('spendTokens', mockSpendTokens); expect(deps).toHaveProperty('spendStructuredTokens', mockSpendStructuredTokens); + expect(deps).toHaveProperty('pricing'); + expect(deps.pricing).toHaveProperty('getMultiplier', mockGetMultiplier); + expect(deps.pricing).toHaveProperty('getCacheMultiplier', mockGetCacheMultiplier); + expect(deps).toHaveProperty('bulkWriteOps'); + expect(deps.bulkWriteOps).toHaveProperty('insertMany', mockBulkInsertTransactions); + expect(deps.bulkWriteOps).toHaveProperty('updateBalance', mockUpdateBalance); }); it('should include model from primaryConfig in recordCollectedUsage params', async () => { @@ -247,7 +349,15 @@ describe('createResponse controller', () => { expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1); expect(mockRecordCollectedUsage).toHaveBeenCalledWith( - { spendTokens: mockSpendTokens, spendStructuredTokens: mockSpendStructuredTokens }, + { + spendTokens: mockSpendTokens, + spendStructuredTokens: mockSpendStructuredTokens, + pricing: { getMultiplier: mockGetMultiplier, getCacheMultiplier: mockGetCacheMultiplier }, + bulkWriteOps: { + insertMany: mockBulkInsertTransactions, + updateBalance: mockUpdateBalance, + }, + }, expect.objectContaining({ user: 'user-123', context: 'message', diff --git a/api/server/controllers/agents/__tests__/v1.duplicate-actions.spec.js b/api/server/controllers/agents/__tests__/v1.duplicate-actions.spec.js new file mode 100644 index 0000000000..cc298bd03a --- /dev/null +++ b/api/server/controllers/agents/__tests__/v1.duplicate-actions.spec.js @@ -0,0 +1,159 @@ +jest.mock('~/server/services/PermissionService', () => ({ + findPubliclyAccessibleResources: jest.fn(), + findAccessibleResources: jest.fn(), + hasPublicPermission: jest.fn(), + grantPermission: jest.fn().mockResolvedValue({}), +})); + +jest.mock('~/server/services/Config', () => ({ + getCachedTools: jest.fn(), + getMCPServerTools: jest.fn(), +})); + +const mongoose = require('mongoose'); +const { actionDelimiter } = require('librechat-data-provider'); +const { agentSchema, actionSchema } = require('@librechat/data-schemas'); +const { MongoMemoryServer } = require('mongodb-memory-server'); +const { duplicateAgent } = require('../v1'); + +let mongoServer; + +beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = mongoServer.getUri(); + if (!mongoose.models.Agent) { + mongoose.model('Agent', agentSchema); + } + if (!mongoose.models.Action) { + mongoose.model('Action', actionSchema); + } + await mongoose.connect(mongoUri); +}, 20000); + +afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); +}); + +beforeEach(async () => { + await mongoose.models.Agent.deleteMany({}); + await mongoose.models.Action.deleteMany({}); +}); + +describe('duplicateAgentHandler — action domain extraction', () => { + it('builds duplicated action entries using metadata.domain, not action_id', async () => { + const userId = new mongoose.Types.ObjectId(); + const originalAgentId = `agent_original`; + + const agent = await mongoose.models.Agent.create({ + id: originalAgentId, + name: 'Test Agent', + author: userId.toString(), + provider: 'openai', + model: 'gpt-4', + tools: [], + actions: [`api.example.com${actionDelimiter}act_original`], + versions: [{ name: 'Test Agent', createdAt: new Date(), updatedAt: new Date() }], + }); + + await mongoose.models.Action.create({ + user: userId, + action_id: 'act_original', + agent_id: originalAgentId, + metadata: { domain: 'api.example.com' }, + }); + + const req = { + params: { id: agent.id }, + user: { id: userId.toString() }, + }; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + await duplicateAgent(req, res); + + expect(res.status).toHaveBeenCalledWith(201); + + const { agent: newAgent, actions: newActions } = res.json.mock.calls[0][0]; + + expect(newAgent.id).not.toBe(originalAgentId); + expect(String(newAgent.author)).toBe(userId.toString()); + expect(newActions).toHaveLength(1); + expect(newActions[0].metadata.domain).toBe('api.example.com'); + expect(newActions[0].agent_id).toBe(newAgent.id); + + for (const actionEntry of newAgent.actions) { + const [domain, actionId] = actionEntry.split(actionDelimiter); + expect(domain).toBe('api.example.com'); + expect(actionId).toBeTruthy(); + expect(actionId).not.toBe('act_original'); + } + + const allActions = await mongoose.models.Action.find({}).lean(); + expect(allActions).toHaveLength(2); + + const originalAction = allActions.find((a) => a.action_id === 'act_original'); + expect(originalAction.agent_id).toBe(originalAgentId); + + const duplicatedAction = allActions.find((a) => a.action_id !== 'act_original'); + expect(duplicatedAction.agent_id).toBe(newAgent.id); + expect(duplicatedAction.metadata.domain).toBe('api.example.com'); + }); + + it('strips sensitive metadata fields from duplicated actions', async () => { + const userId = new mongoose.Types.ObjectId(); + const originalAgentId = 'agent_sensitive'; + + await mongoose.models.Agent.create({ + id: originalAgentId, + name: 'Sensitive Agent', + author: userId.toString(), + provider: 'openai', + model: 'gpt-4', + tools: [], + actions: [`secure.api.com${actionDelimiter}act_secret`], + versions: [{ name: 'Sensitive Agent', createdAt: new Date(), updatedAt: new Date() }], + }); + + await mongoose.models.Action.create({ + user: userId, + action_id: 'act_secret', + agent_id: originalAgentId, + metadata: { + domain: 'secure.api.com', + api_key: 'sk-secret-key-12345', + oauth_client_id: 'client_id_xyz', + oauth_client_secret: 'client_secret_xyz', + }, + }); + + const req = { + params: { id: originalAgentId }, + user: { id: userId.toString() }, + }; + const res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + + await duplicateAgent(req, res); + + expect(res.status).toHaveBeenCalledWith(201); + + const duplicatedAction = await mongoose.models.Action.findOne({ + agent_id: { $ne: originalAgentId }, + }).lean(); + + expect(duplicatedAction.metadata.domain).toBe('secure.api.com'); + expect(duplicatedAction.metadata.api_key).toBeUndefined(); + expect(duplicatedAction.metadata.oauth_client_id).toBeUndefined(); + expect(duplicatedAction.metadata.oauth_client_secret).toBeUndefined(); + + const originalAction = await mongoose.models.Action.findOne({ + action_id: 'act_secret', + }).lean(); + expect(originalAction.metadata.api_key).toBe('sk-secret-key-12345'); + }); +}); diff --git a/api/server/controllers/agents/__tests__/v1.spec.js b/api/server/controllers/agents/__tests__/v1.spec.js index b7e7b67a22..39cf994fef 100644 --- a/api/server/controllers/agents/__tests__/v1.spec.js +++ b/api/server/controllers/agents/__tests__/v1.spec.js @@ -1,10 +1,8 @@ const { duplicateAgent } = require('../v1'); -const { getAgent, createAgent } = require('~/models/Agent'); -const { getActions } = require('~/models/Action'); +const { getAgent, createAgent, getActions } = require('~/models'); const { nanoid } = require('nanoid'); -jest.mock('~/models/Agent'); -jest.mock('~/models/Action'); +jest.mock('~/models'); jest.mock('nanoid'); describe('duplicateAgent', () => { diff --git a/api/server/controllers/agents/callbacks.js b/api/server/controllers/agents/callbacks.js index 163fc4ebba..0bb935795d 100644 --- a/api/server/controllers/agents/callbacks.js +++ b/api/server/controllers/agents/callbacks.js @@ -1,22 +1,13 @@ const { nanoid } = require('nanoid'); -const { Constants } = require('@librechat/agents'); const { logger } = require('@librechat/data-schemas'); +const { Constants, EnvVar, GraphEvents, ToolEndHandler } = require('@librechat/agents'); +const { Tools, StepTypes, FileContext, ErrorTypes } = require('librechat-data-provider'); const { sendEvent, GenerationJobManager, writeAttachmentEvent, createToolExecuteHandler, } = require('@librechat/api'); -const { Tools, StepTypes, FileContext, ErrorTypes } = require('librechat-data-provider'); -const { - EnvVar, - Providers, - GraphEvents, - getMessageId, - ToolEndHandler, - handleToolCalls, - ChatModelStreamHandler, -} = require('@librechat/agents'); const { processFileCitations } = require('~/server/services/Files/Citations'); const { processCodeOutput } = require('~/server/services/Files/Code/process'); const { loadAuthValues } = require('~/server/services/Tools/credentials'); @@ -57,8 +48,6 @@ class ModelEndHandler { let errorMessage; try { const agentContext = graph.getAgentContext(metadata); - const isGoogle = agentContext.provider === Providers.GOOGLE; - const streamingDisabled = !!agentContext.clientOptions?.disableStreaming; if (data?.output?.additional_kwargs?.stop_reason === 'refusal') { const info = { ...data.output.additional_kwargs }; errorMessage = JSON.stringify({ @@ -73,21 +62,6 @@ class ModelEndHandler { }); } - const toolCalls = data?.output?.tool_calls; - let hasUnprocessedToolCalls = false; - if (Array.isArray(toolCalls) && toolCalls.length > 0 && graph?.toolCallStepIds?.has) { - try { - hasUnprocessedToolCalls = toolCalls.some( - (tc) => tc?.id && !graph.toolCallStepIds.has(tc.id), - ); - } catch { - hasUnprocessedToolCalls = false; - } - } - if (isGoogle || streamingDisabled || hasUnprocessedToolCalls) { - await handleToolCalls(toolCalls, metadata, graph); - } - const usage = data?.output?.usage_metadata; if (!usage) { return this.finalize(errorMessage); @@ -98,38 +72,6 @@ class ModelEndHandler { } this.collectedUsage.push(usage); - if (!streamingDisabled) { - return this.finalize(errorMessage); - } - if (!data.output.content) { - return this.finalize(errorMessage); - } - const stepKey = graph.getStepKey(metadata); - const message_id = getMessageId(stepKey, graph) ?? ''; - if (message_id) { - await graph.dispatchRunStep(stepKey, { - type: StepTypes.MESSAGE_CREATION, - message_creation: { - message_id, - }, - }); - } - const stepId = graph.getStepIdByKey(stepKey); - const content = data.output.content; - if (typeof content === 'string') { - await graph.dispatchMessageDelta(stepId, { - content: [ - { - type: 'text', - text: content, - }, - ], - }); - } else if (content.every((c) => c.type?.startsWith('text'))) { - await graph.dispatchMessageDelta(stepId, { - content, - }); - } } catch (error) { logger.error('Error handling model end event:', error); return this.finalize(errorMessage); @@ -200,7 +142,6 @@ function getDefaultHandlers({ const handlers = { [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage), [GraphEvents.TOOL_END]: new ToolEndHandler(toolEndCallback, logger), - [GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(), [GraphEvents.ON_RUN_STEP]: { /** * Handle ON_RUN_STEP event. diff --git a/api/server/controllers/agents/client.js b/api/server/controllers/agents/client.js index d90e9b370e..cbd01b86ec 100644 --- a/api/server/controllers/agents/client.js +++ b/api/server/controllers/agents/client.js @@ -6,18 +6,24 @@ const { Tokenizer, checkAccess, buildToolSet, - logAxiosError, sanitizeTitle, + logToolError, + payloadParser, resolveHeaders, createSafeUser, initializeAgent, getBalanceConfig, + omitTitleOptions, getProviderConfig, memoryInstructions, + createTokenCounter, applyContextToAgent, + recordCollectedUsage, GenerationJobManager, getTransactionsConfig, createMemoryProcessor, + loadAgent: loadAgentFn, + createMultiAgentMapper, filterMalformedContentParts, } = require('@librechat/api'); const { @@ -25,9 +31,7 @@ const { Providers, TitleMethod, formatMessage, - labelContentByAgent, formatAgentMessages, - getTokenCountForMessage, createMetadataAggregator, } = require('@librechat/agents'); const { @@ -39,195 +43,17 @@ const { PermissionTypes, isAgentsEndpoint, isEphemeralAgentId, - bedrockInputSchema, removeNullishValues, } = require('librechat-data-provider'); -const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens'); +const { filterFilesByAgentAccess } = require('~/server/services/Files/permissions'); const { encodeAndFormat } = require('~/server/services/Files/images/encode'); const { createContextHandlers } = require('~/app/clients/prompts'); -const { getConvoFiles } = require('~/models/Conversation'); +const { getMCPServerTools } = require('~/server/services/Config'); const BaseClient = require('~/app/clients/BaseClient'); -const { getRoleByName } = require('~/models/Role'); -const { loadAgent } = require('~/models/Agent'); const { getMCPManager } = require('~/config'); const db = require('~/models'); -const omitTitleOptions = new Set([ - 'stream', - 'thinking', - 'streaming', - 'clientOptions', - 'thinkingConfig', - 'thinkingBudget', - 'includeThoughts', - 'maxOutputTokens', - 'additionalModelRequestFields', -]); - -/** - * @param {ServerRequest} req - * @param {Agent} agent - * @param {string} endpoint - */ -const payloadParser = ({ req, agent, endpoint }) => { - if (isAgentsEndpoint(endpoint)) { - return { model: undefined }; - } else if (endpoint === EModelEndpoint.bedrock) { - const parsedValues = bedrockInputSchema.parse(agent.model_parameters); - if (parsedValues.thinking == null) { - parsedValues.thinking = false; - } - return parsedValues; - } - return req.body.endpointOption.model_parameters; -}; - -function createTokenCounter(encoding) { - return function (message) { - const countTokens = (text) => Tokenizer.getTokenCount(text, encoding); - return getTokenCountForMessage(message, countTokens); - }; -} - -function logToolError(graph, error, toolId) { - logAxiosError({ - error, - message: `[api/server/controllers/agents/client.js #chatCompletion] Tool Error "${toolId}"`, - }); -} - -/** Regex pattern to match agent ID suffix (____N) */ -const AGENT_SUFFIX_PATTERN = /____(\d+)$/; - -/** - * Finds the primary agent ID within a set of agent IDs. - * Primary = no suffix (____N) or lowest suffix number. - * @param {Set} agentIds - * @returns {string | null} - */ -function findPrimaryAgentId(agentIds) { - let primaryAgentId = null; - let lowestSuffixIndex = Infinity; - - for (const agentId of agentIds) { - const suffixMatch = agentId.match(AGENT_SUFFIX_PATTERN); - if (!suffixMatch) { - return agentId; - } - const suffixIndex = parseInt(suffixMatch[1], 10); - if (suffixIndex < lowestSuffixIndex) { - lowestSuffixIndex = suffixIndex; - primaryAgentId = agentId; - } - } - - return primaryAgentId; -} - -/** - * Creates a mapMethod for getMessagesForConversation that processes agent content. - * - Strips agentId/groupId metadata from all content - * - For parallel agents (addedConvo with groupId): filters each group to its primary agent - * - For handoffs (agentId without groupId): keeps all content from all agents - * - For multi-agent: applies agent labels to content - * - * The key distinction: - * - Parallel execution (addedConvo): Parts have both agentId AND groupId - * - Handoffs: Parts only have agentId, no groupId - * - * @param {Agent} primaryAgent - Primary agent configuration - * @param {Map} [agentConfigs] - Additional agent configurations - * @returns {(message: TMessage) => TMessage} Map method for processing messages - */ -function createMultiAgentMapper(primaryAgent, agentConfigs) { - const hasMultipleAgents = (primaryAgent.edges?.length ?? 0) > 0 || (agentConfigs?.size ?? 0) > 0; - - /** @type {Record | null} */ - let agentNames = null; - if (hasMultipleAgents) { - agentNames = { [primaryAgent.id]: primaryAgent.name || 'Assistant' }; - if (agentConfigs) { - for (const [agentId, agentConfig] of agentConfigs.entries()) { - agentNames[agentId] = agentConfig.name || agentConfig.id; - } - } - } - - return (message) => { - if (message.isCreatedByUser || !Array.isArray(message.content)) { - return message; - } - - // Check for metadata - const hasAgentMetadata = message.content.some((part) => part?.agentId || part?.groupId != null); - if (!hasAgentMetadata) { - return message; - } - - try { - // Build a map of groupId -> Set of agentIds, to find primary per group - /** @type {Map>} */ - const groupAgentMap = new Map(); - - for (const part of message.content) { - const groupId = part?.groupId; - const agentId = part?.agentId; - if (groupId != null && agentId) { - if (!groupAgentMap.has(groupId)) { - groupAgentMap.set(groupId, new Set()); - } - groupAgentMap.get(groupId).add(agentId); - } - } - - // For each group, find the primary agent - /** @type {Map} */ - const groupPrimaryMap = new Map(); - for (const [groupId, agentIds] of groupAgentMap) { - const primary = findPrimaryAgentId(agentIds); - if (primary) { - groupPrimaryMap.set(groupId, primary); - } - } - - /** @type {Array} */ - const filteredContent = []; - /** @type {Record} */ - const agentIdMap = {}; - - for (const part of message.content) { - const agentId = part?.agentId; - const groupId = part?.groupId; - - // Filtering logic: - // - No groupId (handoffs): always include - // - Has groupId (parallel): only include if it's the primary for that group - const isParallelPart = groupId != null; - const groupPrimary = isParallelPart ? groupPrimaryMap.get(groupId) : null; - const shouldInclude = !isParallelPart || !agentId || agentId === groupPrimary; - - if (shouldInclude) { - const newIndex = filteredContent.length; - const { agentId: _a, groupId: _g, ...cleanPart } = part; - filteredContent.push(cleanPart); - if (agentId && hasMultipleAgents) { - agentIdMap[newIndex] = agentId; - } - } - } - - const finalContent = - Object.keys(agentIdMap).length > 0 && agentNames - ? labelContentByAgent(filteredContent, agentIdMap, agentNames) - : filteredContent; - - return { ...message, content: finalContent }; - } catch (error) { - logger.error('[AgentClient] Error processing multi-agent message:', error); - return message; - } - }; -} +const loadAgent = (params) => loadAgentFn(params, { getAgent: db.getAgent, getMCPServerTools }); class AgentClient extends BaseClient { constructor(options = {}) { @@ -296,14 +122,9 @@ class AgentClient extends BaseClient { checkVisionRequest() {} getSaveOptions() { - // TODO: - // would need to be override settings; otherwise, model needs to be undefined - // model: this.override.model, - // instructions: this.override.instructions, - // additional_instructions: this.override.additional_instructions, let runOptions = {}; try { - runOptions = payloadParser(this.options); + runOptions = payloadParser(this.options) ?? {}; } catch (error) { logger.error( '[api/server/controllers/agents/client.js #getSaveOptions] Error parsing options', @@ -314,16 +135,16 @@ class AgentClient extends BaseClient { return removeNullishValues( Object.assign( { + spec: this.options.spec, + iconURL: this.options.iconURL, endpoint: this.options.endpoint, endpointType: this.options.endpointType, model: this.options.agent?.model_parameters?.model, agent_id: this.options.agent.id, modelLabel: this.options.modelLabel, - maxContextTokens: this.options.maxContextTokens, resendFiles: this.options.resendFiles, imageDetail: this.options.imageDetail, - spec: this.options.spec, - iconURL: this.options.iconURL, + maxContextTokens: this.maxContextTokens, }, // TODO: PARSE OPTIONS BY PROVIDER, MAY CONTAIN SENSITIVE DATA runOptions, @@ -592,7 +413,7 @@ class AgentClient extends BaseClient { user, permissionType: PermissionTypes.MEMORIES, permissions: [Permissions.USE], - getRoleByName, + getRoleByName: db.getRoleByName, }); if (!hasAccess) { @@ -652,13 +473,14 @@ class AgentClient extends BaseClient { }, }, { - getConvoFiles, getFiles: db.getFiles, getUserKey: db.getUserKey, + getConvoFiles: db.getConvoFiles, updateFilesUsage: db.updateFilesUsage, getUserKeyValues: db.getUserKeyValues, getToolFilesByIds: db.getToolFilesByIds, getCodeGeneratedFiles: db.getCodeGeneratedFiles, + filterFilesByAgentAccess, }, ); @@ -807,82 +629,29 @@ class AgentClient extends BaseClient { context = 'message', collectedUsage = this.collectedUsage, }) { - if (!collectedUsage || !collectedUsage.length) { - return; - } - // Use first entry's input_tokens as the base input (represents initial user message context) - // Support both OpenAI format (input_token_details) and Anthropic format (cache_*_input_tokens) - const firstUsage = collectedUsage[0]; - const input_tokens = - (firstUsage?.input_tokens || 0) + - (Number(firstUsage?.input_token_details?.cache_creation) || - Number(firstUsage?.cache_creation_input_tokens) || - 0) + - (Number(firstUsage?.input_token_details?.cache_read) || - Number(firstUsage?.cache_read_input_tokens) || - 0); - - // Sum output_tokens directly from all entries - works for both sequential and parallel execution - // This avoids the incremental calculation that produced negative values for parallel agents - let total_output_tokens = 0; - - for (const usage of collectedUsage) { - if (!usage) { - continue; - } - - // Support both OpenAI format (input_token_details) and Anthropic format (cache_*_input_tokens) - const cache_creation = - Number(usage.input_token_details?.cache_creation) || - Number(usage.cache_creation_input_tokens) || - 0; - const cache_read = - Number(usage.input_token_details?.cache_read) || Number(usage.cache_read_input_tokens) || 0; - - // Accumulate output tokens for the usage summary - total_output_tokens += Number(usage.output_tokens) || 0; - - const txMetadata = { + const result = await recordCollectedUsage( + { + spendTokens: db.spendTokens, + spendStructuredTokens: db.spendStructuredTokens, + pricing: { getMultiplier: db.getMultiplier, getCacheMultiplier: db.getCacheMultiplier }, + bulkWriteOps: { insertMany: db.bulkInsertTransactions, updateBalance: db.updateBalance }, + }, + { + user: this.user ?? this.options.req.user?.id, + conversationId: this.conversationId, + collectedUsage, + model: model ?? this.model ?? this.options.agent.model_parameters.model, context, + messageId: this.responseMessageId, balance, transactions, - conversationId: this.conversationId, - user: this.user ?? this.options.req.user?.id, endpointTokenConfig: this.options.endpointTokenConfig, - model: usage.model ?? model ?? this.model ?? this.options.agent.model_parameters.model, - }; + }, + ); - if (cache_creation > 0 || cache_read > 0) { - spendStructuredTokens(txMetadata, { - promptTokens: { - input: usage.input_tokens, - write: cache_creation, - read: cache_read, - }, - completionTokens: usage.output_tokens, - }).catch((err) => { - logger.error( - '[api/server/controllers/agents/client.js #recordCollectedUsage] Error spending structured tokens', - err, - ); - }); - continue; - } - spendTokens(txMetadata, { - promptTokens: usage.input_tokens, - completionTokens: usage.output_tokens, - }).catch((err) => { - logger.error( - '[api/server/controllers/agents/client.js #recordCollectedUsage] Error spending tokens', - err, - ); - }); + if (result) { + this.usage = result; } - - this.usage = { - input_tokens, - output_tokens: total_output_tokens, - }; } /** @@ -1074,9 +843,10 @@ class AgentClient extends BaseClient { config.signal = null; }; + const hideSequentialOutputs = config.configurable.hide_sequential_outputs; await runAgents(initialMessages); /** @deprecated Agent Chain */ - if (config.configurable.hide_sequential_outputs) { + if (hideSequentialOutputs) { this.contentParts = this.contentParts.filter((part, index) => { // Include parts that are either: // 1. At or after the finalContentStart index @@ -1330,6 +1100,7 @@ class AgentClient extends BaseClient { model: clientOptions.model, balance: balanceConfig, transactions: transactionsConfig, + messageId: this.responseMessageId, }).catch((err) => { logger.error( '[api/server/controllers/agents/client.js #titleConvo] Error recording collected usage', @@ -1363,11 +1134,12 @@ class AgentClient extends BaseClient { context = 'message', }) { try { - await spendTokens( + await db.spendTokens( { model, context, balance, + messageId: this.responseMessageId, conversationId: this.conversationId, user: this.user ?? this.options.req.user?.id, endpointTokenConfig: this.options.endpointTokenConfig, @@ -1381,11 +1153,12 @@ class AgentClient extends BaseClient { 'reasoning_tokens' in usage && typeof usage.reasoning_tokens === 'number' ) { - await spendTokens( + await db.spendTokens( { model, balance, context: 'reasoning', + messageId: this.responseMessageId, conversationId: this.conversationId, user: this.user ?? this.options.req.user?.id, endpointTokenConfig: this.options.endpointTokenConfig, @@ -1401,7 +1174,11 @@ class AgentClient extends BaseClient { } } + /** Anthropic Claude models use a distinct BPE tokenizer; all others default to o200k_base. */ getEncoding() { + if (this.model && this.model.toLowerCase().includes('claude')) { + return 'claude'; + } return 'o200k_base'; } diff --git a/api/server/controllers/agents/client.test.js b/api/server/controllers/agents/client.test.js index 9dd3567047..4e3d10e8e6 100644 --- a/api/server/controllers/agents/client.test.js +++ b/api/server/controllers/agents/client.test.js @@ -15,13 +15,15 @@ jest.mock('@librechat/api', () => ({ checkAccess: jest.fn(), initializeAgent: jest.fn(), createMemoryProcessor: jest.fn(), -})); - -jest.mock('~/models/Agent', () => ({ loadAgent: jest.fn(), })); -jest.mock('~/models/Role', () => ({ +jest.mock('~/server/services/Config', () => ({ + getMCPServerTools: jest.fn(), +})); + +jest.mock('~/models', () => ({ + getAgent: jest.fn(), getRoleByName: jest.fn(), })); @@ -263,6 +265,7 @@ describe('AgentClient - titleConvo', () => { transactions: { enabled: true, }, + messageId: 'response-123', }); }); @@ -2137,7 +2140,7 @@ describe('AgentClient - titleConvo', () => { }; mockCheckAccess = require('@librechat/api').checkAccess; - mockLoadAgent = require('~/models/Agent').loadAgent; + mockLoadAgent = require('@librechat/api').loadAgent; mockInitializeAgent = require('@librechat/api').initializeAgent; mockCreateMemoryProcessor = require('@librechat/api').createMemoryProcessor; }); @@ -2194,6 +2197,7 @@ describe('AgentClient - titleConvo', () => { expect.objectContaining({ agent_id: differentAgentId, }), + expect.any(Object), ); expect(mockInitializeAgent).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/api/server/controllers/agents/errors.js b/api/server/controllers/agents/errors.js index 54b296a5d2..b16ce75591 100644 --- a/api/server/controllers/agents/errors.js +++ b/api/server/controllers/agents/errors.js @@ -3,8 +3,8 @@ const { logger } = require('@librechat/data-schemas'); const { CacheKeys, ViolationTypes } = require('librechat-data-provider'); const { sendResponse } = require('~/server/middleware/error'); const { recordUsage } = require('~/server/services/Threads'); -const { getConvo } = require('~/models/Conversation'); const getLogStores = require('~/cache/getLogStores'); +const { getConvo } = require('~/models'); /** * @typedef {Object} ErrorHandlerContext diff --git a/api/server/controllers/agents/filterAuthorizedTools.spec.js b/api/server/controllers/agents/filterAuthorizedTools.spec.js new file mode 100644 index 0000000000..e215fdc1fc --- /dev/null +++ b/api/server/controllers/agents/filterAuthorizedTools.spec.js @@ -0,0 +1,668 @@ +const mongoose = require('mongoose'); +const { v4: uuidv4 } = require('uuid'); +const { Constants } = require('librechat-data-provider'); +const { agentSchema } = require('@librechat/data-schemas'); +const { MongoMemoryServer } = require('mongodb-memory-server'); + +const d = Constants.mcp_delimiter; + +const mockGetAllServerConfigs = jest.fn(); + +jest.mock('~/server/services/Config', () => ({ + getCachedTools: jest.fn().mockResolvedValue({ + web_search: true, + execute_code: true, + file_search: true, + }), +})); + +jest.mock('~/config', () => ({ + getMCPServersRegistry: jest.fn(() => ({ + getAllServerConfigs: mockGetAllServerConfigs, + })), +})); + +jest.mock('~/server/services/Files/strategies', () => ({ + getStrategyFunctions: jest.fn(), +})); + +jest.mock('~/server/services/Files/images/avatar', () => ({ + resizeAvatar: jest.fn(), +})); + +jest.mock('~/server/services/Files/process', () => ({ + filterFile: jest.fn(), +})); + +jest.mock('~/server/services/PermissionService', () => ({ + findAccessibleResources: jest.fn().mockResolvedValue([]), + findPubliclyAccessibleResources: jest.fn().mockResolvedValue([]), + grantPermission: jest.fn(), + hasPublicPermission: jest.fn().mockResolvedValue(false), + checkPermission: jest.fn().mockResolvedValue(true), +})); + +jest.mock('~/models', () => { + const mongoose = require('mongoose'); + const { createModels, createMethods } = require('@librechat/data-schemas'); + createModels(mongoose); + const methods = createMethods(mongoose); + return { + ...methods, + getCategoriesWithCounts: jest.fn(), + deleteFileByFilter: jest.fn(), + }; +}); + +jest.mock('~/cache', () => ({ + getLogStores: jest.fn(() => ({ + get: jest.fn(), + set: jest.fn(), + delete: jest.fn(), + })), +})); + +const { + filterAuthorizedTools, + createAgent: createAgentHandler, + updateAgent: updateAgentHandler, + duplicateAgent: duplicateAgentHandler, + revertAgentVersion: revertAgentVersionHandler, +} = require('./v1'); + +const { getMCPServersRegistry } = require('~/config'); + +let Agent; + +describe('MCP Tool Authorization', () => { + let mongoServer; + let mockReq; + let mockRes; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = mongoServer.getUri(); + await mongoose.connect(mongoUri); + Agent = mongoose.models.Agent || mongoose.model('Agent', agentSchema); + }, 20000); + + afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); + }); + + beforeEach(async () => { + await Agent.deleteMany({}); + jest.clearAllMocks(); + + getMCPServersRegistry.mockImplementation(() => ({ + getAllServerConfigs: mockGetAllServerConfigs, + })); + mockGetAllServerConfigs.mockResolvedValue({ + authorizedServer: { type: 'sse', url: 'https://authorized.example.com' }, + anotherServer: { type: 'sse', url: 'https://another.example.com' }, + }); + + mockReq = { + user: { + id: new mongoose.Types.ObjectId().toString(), + role: 'USER', + }, + body: {}, + params: {}, + query: {}, + app: { locals: { fileStrategy: 'local' } }, + }; + + mockRes = { + status: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), + }; + }); + + describe('filterAuthorizedTools', () => { + const availableTools = { web_search: true, custom_tool: true }; + const userId = 'test-user-123'; + + test('should keep authorized MCP tools and strip unauthorized ones', async () => { + const result = await filterAuthorizedTools({ + tools: [`toolA${d}authorizedServer`, `toolB${d}forbiddenServer`, 'web_search'], + userId, + availableTools, + }); + + expect(result).toContain(`toolA${d}authorizedServer`); + expect(result).toContain('web_search'); + expect(result).not.toContain(`toolB${d}forbiddenServer`); + }); + + test('should keep system tools without querying MCP registry', async () => { + const result = await filterAuthorizedTools({ + tools: ['execute_code', 'file_search', 'web_search'], + userId, + availableTools: {}, + }); + + expect(result).toEqual(['execute_code', 'file_search', 'web_search']); + expect(mockGetAllServerConfigs).not.toHaveBeenCalled(); + }); + + test('should not query MCP registry when no MCP tools are present', async () => { + const result = await filterAuthorizedTools({ + tools: ['web_search', 'custom_tool'], + userId, + availableTools, + }); + + expect(result).toEqual(['web_search', 'custom_tool']); + expect(mockGetAllServerConfigs).not.toHaveBeenCalled(); + }); + + test('should filter all MCP tools when registry is uninitialized', async () => { + getMCPServersRegistry.mockImplementation(() => { + throw new Error('MCPServersRegistry has not been initialized.'); + }); + + const result = await filterAuthorizedTools({ + tools: [`toolA${d}someServer`, 'web_search'], + userId, + availableTools, + }); + + expect(result).toEqual(['web_search']); + expect(result).not.toContain(`toolA${d}someServer`); + }); + + test('should handle mixed authorized and unauthorized MCP tools', async () => { + const result = await filterAuthorizedTools({ + tools: [ + 'web_search', + `search${d}authorizedServer`, + `attack${d}victimServer`, + 'execute_code', + `list${d}anotherServer`, + `steal${d}nonexistent`, + ], + userId, + availableTools, + }); + + expect(result).toEqual([ + 'web_search', + `search${d}authorizedServer`, + 'execute_code', + `list${d}anotherServer`, + ]); + }); + + test('should handle empty tools array', async () => { + const result = await filterAuthorizedTools({ + tools: [], + userId, + availableTools, + }); + + expect(result).toEqual([]); + expect(mockGetAllServerConfigs).not.toHaveBeenCalled(); + }); + + test('should handle null/undefined tool entries gracefully', async () => { + const result = await filterAuthorizedTools({ + tools: [null, undefined, '', 'web_search'], + userId, + availableTools, + }); + + expect(result).toEqual(['web_search']); + }); + + test('should call getAllServerConfigs with the correct userId', async () => { + await filterAuthorizedTools({ + tools: [`tool${d}authorizedServer`], + userId: 'specific-user-id', + availableTools, + }); + + expect(mockGetAllServerConfigs).toHaveBeenCalledWith('specific-user-id'); + }); + + test('should only call getAllServerConfigs once even with multiple MCP tools', async () => { + await filterAuthorizedTools({ + tools: [`tool1${d}authorizedServer`, `tool2${d}anotherServer`, `tool3${d}unknownServer`], + userId, + availableTools, + }); + + expect(mockGetAllServerConfigs).toHaveBeenCalledTimes(1); + }); + + test('should preserve existing MCP tools when registry is unavailable', async () => { + getMCPServersRegistry.mockImplementation(() => { + throw new Error('MCPServersRegistry has not been initialized.'); + }); + + const existingTools = [`toolA${d}serverA`, `toolB${d}serverB`]; + + const result = await filterAuthorizedTools({ + tools: [...existingTools, `newTool${d}unknownServer`, 'web_search'], + userId, + availableTools, + existingTools, + }); + + expect(result).toContain(`toolA${d}serverA`); + expect(result).toContain(`toolB${d}serverB`); + expect(result).toContain('web_search'); + expect(result).not.toContain(`newTool${d}unknownServer`); + }); + + test('should still reject all MCP tools when registry is unavailable and no existingTools', async () => { + getMCPServersRegistry.mockImplementation(() => { + throw new Error('MCPServersRegistry has not been initialized.'); + }); + + const result = await filterAuthorizedTools({ + tools: [`toolA${d}serverA`, 'web_search'], + userId, + availableTools, + }); + + expect(result).toEqual(['web_search']); + }); + + test('should not preserve malformed existing tools when registry is unavailable', async () => { + getMCPServersRegistry.mockImplementation(() => { + throw new Error('MCPServersRegistry has not been initialized.'); + }); + + const malformedTool = `a${d}b${d}c`; + const result = await filterAuthorizedTools({ + tools: [malformedTool, `legit${d}serverA`, 'web_search'], + userId, + availableTools, + existingTools: [malformedTool, `legit${d}serverA`], + }); + + expect(result).toContain(`legit${d}serverA`); + expect(result).toContain('web_search'); + expect(result).not.toContain(malformedTool); + }); + + test('should reject malformed MCP tool keys with multiple delimiters', async () => { + const result = await filterAuthorizedTools({ + tools: [ + `attack${d}victimServer${d}authorizedServer`, + `legit${d}authorizedServer`, + `a${d}b${d}c${d}d`, + 'web_search', + ], + userId, + availableTools, + }); + + expect(result).toEqual([`legit${d}authorizedServer`, 'web_search']); + expect(result).not.toContainEqual(expect.stringContaining('victimServer')); + expect(result).not.toContainEqual(expect.stringContaining(`a${d}b`)); + }); + }); + + describe('createAgentHandler - MCP tool authorization', () => { + test('should strip unauthorized MCP tools on create', async () => { + mockReq.body = { + provider: 'openai', + model: 'gpt-4', + name: 'MCP Test Agent', + tools: ['web_search', `validTool${d}authorizedServer`, `attack${d}forbiddenServer`], + }; + + await createAgentHandler(mockReq, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(201); + const agent = mockRes.json.mock.calls[0][0]; + expect(agent.tools).toContain('web_search'); + expect(agent.tools).toContain(`validTool${d}authorizedServer`); + expect(agent.tools).not.toContain(`attack${d}forbiddenServer`); + }); + + test('should not 500 when MCP registry is uninitialized', async () => { + getMCPServersRegistry.mockImplementation(() => { + throw new Error('MCPServersRegistry has not been initialized.'); + }); + + mockReq.body = { + provider: 'openai', + model: 'gpt-4', + name: 'MCP Uninitialized Test', + tools: [`tool${d}someServer`, 'web_search'], + }; + + await createAgentHandler(mockReq, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(201); + const agent = mockRes.json.mock.calls[0][0]; + expect(agent.tools).toEqual(['web_search']); + }); + + test('should store mcpServerNames only for authorized servers', async () => { + mockReq.body = { + provider: 'openai', + model: 'gpt-4', + name: 'MCP Names Test', + tools: [`toolA${d}authorizedServer`, `toolB${d}forbiddenServer`], + }; + + await createAgentHandler(mockReq, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(201); + const agent = mockRes.json.mock.calls[0][0]; + const agentInDb = await Agent.findOne({ id: agent.id }); + expect(agentInDb.mcpServerNames).toContain('authorizedServer'); + expect(agentInDb.mcpServerNames).not.toContain('forbiddenServer'); + }); + }); + + describe('updateAgentHandler - MCP tool authorization', () => { + let existingAgentId; + let existingAgentAuthorId; + + beforeEach(async () => { + existingAgentAuthorId = new mongoose.Types.ObjectId(); + const agent = await Agent.create({ + id: `agent_${uuidv4()}`, + name: 'Original Agent', + provider: 'openai', + model: 'gpt-4', + author: existingAgentAuthorId, + tools: ['web_search', `existingTool${d}authorizedServer`], + mcpServerNames: ['authorizedServer'], + versions: [ + { + name: 'Original Agent', + provider: 'openai', + model: 'gpt-4', + tools: ['web_search', `existingTool${d}authorizedServer`], + createdAt: new Date(), + updatedAt: new Date(), + }, + ], + }); + existingAgentId = agent.id; + }); + + test('should preserve existing MCP tools even if editor lacks access', async () => { + mockGetAllServerConfigs.mockResolvedValue({}); + + mockReq.user.id = existingAgentAuthorId.toString(); + mockReq.params.id = existingAgentId; + mockReq.body = { + tools: ['web_search', `existingTool${d}authorizedServer`], + }; + + await updateAgentHandler(mockReq, mockRes); + + expect(mockRes.json).toHaveBeenCalled(); + const updatedAgent = mockRes.json.mock.calls[0][0]; + expect(updatedAgent.tools).toContain(`existingTool${d}authorizedServer`); + expect(updatedAgent.tools).toContain('web_search'); + }); + + test('should reject newly added unauthorized MCP tools', async () => { + mockReq.user.id = existingAgentAuthorId.toString(); + mockReq.params.id = existingAgentId; + mockReq.body = { + tools: ['web_search', `existingTool${d}authorizedServer`, `attack${d}forbiddenServer`], + }; + + await updateAgentHandler(mockReq, mockRes); + + expect(mockRes.json).toHaveBeenCalled(); + const updatedAgent = mockRes.json.mock.calls[0][0]; + expect(updatedAgent.tools).toContain('web_search'); + expect(updatedAgent.tools).toContain(`existingTool${d}authorizedServer`); + expect(updatedAgent.tools).not.toContain(`attack${d}forbiddenServer`); + }); + + test('should allow adding authorized MCP tools', async () => { + mockReq.user.id = existingAgentAuthorId.toString(); + mockReq.params.id = existingAgentId; + mockReq.body = { + tools: ['web_search', `existingTool${d}authorizedServer`, `newTool${d}anotherServer`], + }; + + await updateAgentHandler(mockReq, mockRes); + + expect(mockRes.json).toHaveBeenCalled(); + const updatedAgent = mockRes.json.mock.calls[0][0]; + expect(updatedAgent.tools).toContain(`newTool${d}anotherServer`); + }); + + test('should not query MCP registry when no new MCP tools added', async () => { + mockReq.user.id = existingAgentAuthorId.toString(); + mockReq.params.id = existingAgentId; + mockReq.body = { + tools: ['web_search', `existingTool${d}authorizedServer`], + }; + + await updateAgentHandler(mockReq, mockRes); + + expect(mockGetAllServerConfigs).not.toHaveBeenCalled(); + }); + + test('should preserve existing MCP tools when registry unavailable and user edits agent', async () => { + getMCPServersRegistry.mockImplementation(() => { + throw new Error('MCPServersRegistry has not been initialized.'); + }); + + mockReq.user.id = existingAgentAuthorId.toString(); + mockReq.params.id = existingAgentId; + mockReq.body = { + name: 'Renamed After Restart', + tools: ['web_search', `existingTool${d}authorizedServer`], + }; + + await updateAgentHandler(mockReq, mockRes); + + expect(mockRes.json).toHaveBeenCalled(); + const updatedAgent = mockRes.json.mock.calls[0][0]; + expect(updatedAgent.tools).toContain(`existingTool${d}authorizedServer`); + expect(updatedAgent.tools).toContain('web_search'); + expect(updatedAgent.name).toBe('Renamed After Restart'); + }); + + test('should preserve existing MCP tools when server not in configs (disconnected)', async () => { + mockGetAllServerConfigs.mockResolvedValue({}); + + mockReq.user.id = existingAgentAuthorId.toString(); + mockReq.params.id = existingAgentId; + mockReq.body = { + name: 'Edited While Disconnected', + tools: ['web_search', `existingTool${d}authorizedServer`], + }; + + await updateAgentHandler(mockReq, mockRes); + + expect(mockRes.json).toHaveBeenCalled(); + const updatedAgent = mockRes.json.mock.calls[0][0]; + expect(updatedAgent.tools).toContain(`existingTool${d}authorizedServer`); + expect(updatedAgent.name).toBe('Edited While Disconnected'); + }); + }); + + describe('duplicateAgentHandler - MCP tool authorization', () => { + let sourceAgentId; + let sourceAgentAuthorId; + + beforeEach(async () => { + sourceAgentAuthorId = new mongoose.Types.ObjectId(); + const agent = await Agent.create({ + id: `agent_${uuidv4()}`, + name: 'Source Agent', + provider: 'openai', + model: 'gpt-4', + author: sourceAgentAuthorId, + tools: ['web_search', `tool${d}authorizedServer`, `tool${d}forbiddenServer`], + mcpServerNames: ['authorizedServer', 'forbiddenServer'], + versions: [ + { + name: 'Source Agent', + provider: 'openai', + model: 'gpt-4', + tools: ['web_search', `tool${d}authorizedServer`, `tool${d}forbiddenServer`], + createdAt: new Date(), + updatedAt: new Date(), + }, + ], + }); + sourceAgentId = agent.id; + }); + + test('should strip unauthorized MCP tools from duplicated agent', async () => { + mockGetAllServerConfigs.mockResolvedValue({ + authorizedServer: { type: 'sse' }, + }); + + mockReq.user.id = sourceAgentAuthorId.toString(); + mockReq.params.id = sourceAgentId; + + await duplicateAgentHandler(mockReq, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(201); + const { agent: newAgent } = mockRes.json.mock.calls[0][0]; + expect(newAgent.id).not.toBe(sourceAgentId); + expect(newAgent.tools).toContain('web_search'); + expect(newAgent.tools).toContain(`tool${d}authorizedServer`); + expect(newAgent.tools).not.toContain(`tool${d}forbiddenServer`); + + const agentInDb = await Agent.findOne({ id: newAgent.id }); + expect(agentInDb.mcpServerNames).toContain('authorizedServer'); + expect(agentInDb.mcpServerNames).not.toContain('forbiddenServer'); + }); + + test('should preserve source agent MCP tools when registry is unavailable', async () => { + getMCPServersRegistry.mockImplementation(() => { + throw new Error('MCPServersRegistry has not been initialized.'); + }); + + mockReq.user.id = sourceAgentAuthorId.toString(); + mockReq.params.id = sourceAgentId; + + await duplicateAgentHandler(mockReq, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(201); + const { agent: newAgent } = mockRes.json.mock.calls[0][0]; + expect(newAgent.tools).toContain('web_search'); + expect(newAgent.tools).toContain(`tool${d}authorizedServer`); + expect(newAgent.tools).toContain(`tool${d}forbiddenServer`); + }); + }); + + describe('revertAgentVersionHandler - MCP tool authorization', () => { + let existingAgentId; + let existingAgentAuthorId; + + beforeEach(async () => { + existingAgentAuthorId = new mongoose.Types.ObjectId(); + const agent = await Agent.create({ + id: `agent_${uuidv4()}`, + name: 'Reverted Agent V2', + provider: 'openai', + model: 'gpt-4', + author: existingAgentAuthorId, + tools: ['web_search'], + versions: [ + { + name: 'Reverted Agent V1', + provider: 'openai', + model: 'gpt-4', + tools: ['web_search', `oldTool${d}revokedServer`], + createdAt: new Date(Date.now() - 10000), + updatedAt: new Date(Date.now() - 10000), + }, + { + name: 'Reverted Agent V2', + provider: 'openai', + model: 'gpt-4', + tools: ['web_search'], + createdAt: new Date(), + updatedAt: new Date(), + }, + ], + }); + existingAgentId = agent.id; + }); + + test('should strip unauthorized MCP tools after reverting to a previous version', async () => { + mockGetAllServerConfigs.mockResolvedValue({ + authorizedServer: { type: 'sse' }, + }); + + mockReq.user.id = existingAgentAuthorId.toString(); + mockReq.params.id = existingAgentId; + mockReq.body = { version_index: 0 }; + + await revertAgentVersionHandler(mockReq, mockRes); + + expect(mockRes.json).toHaveBeenCalled(); + const result = mockRes.json.mock.calls[0][0]; + expect(result.tools).toContain('web_search'); + expect(result.tools).not.toContain(`oldTool${d}revokedServer`); + + const agentInDb = await Agent.findOne({ id: existingAgentId }); + expect(agentInDb.tools).toContain('web_search'); + expect(agentInDb.tools).not.toContain(`oldTool${d}revokedServer`); + }); + + test('should keep authorized MCP tools after revert', async () => { + await Agent.updateOne( + { id: existingAgentId }, + { $set: { 'versions.0.tools': ['web_search', `tool${d}authorizedServer`] } }, + ); + + mockReq.user.id = existingAgentAuthorId.toString(); + mockReq.params.id = existingAgentId; + mockReq.body = { version_index: 0 }; + + await revertAgentVersionHandler(mockReq, mockRes); + + expect(mockRes.json).toHaveBeenCalled(); + const result = mockRes.json.mock.calls[0][0]; + expect(result.tools).toContain('web_search'); + expect(result.tools).toContain(`tool${d}authorizedServer`); + }); + + test('should preserve version MCP tools when registry is unavailable on revert', async () => { + await Agent.updateOne( + { id: existingAgentId }, + { + $set: { + 'versions.0.tools': [ + 'web_search', + `validTool${d}authorizedServer`, + `otherTool${d}anotherServer`, + ], + }, + }, + ); + + getMCPServersRegistry.mockImplementation(() => { + throw new Error('MCPServersRegistry has not been initialized.'); + }); + + mockReq.user.id = existingAgentAuthorId.toString(); + mockReq.params.id = existingAgentId; + mockReq.body = { version_index: 0 }; + + await revertAgentVersionHandler(mockReq, mockRes); + + expect(mockRes.json).toHaveBeenCalled(); + const result = mockRes.json.mock.calls[0][0]; + expect(result.tools).toContain('web_search'); + expect(result.tools).toContain(`validTool${d}authorizedServer`); + expect(result.tools).toContain(`otherTool${d}anotherServer`); + + const agentInDb = await Agent.findOne({ id: existingAgentId }); + expect(agentInDb.tools).toContain(`validTool${d}authorizedServer`); + expect(agentInDb.tools).toContain(`otherTool${d}anotherServer`); + }); + }); +}); diff --git a/api/server/controllers/agents/openai.js b/api/server/controllers/agents/openai.js index d4dc82174d..ae2e462103 100644 --- a/api/server/controllers/agents/openai.js +++ b/api/server/controllers/agents/openai.js @@ -1,12 +1,7 @@ const { nanoid } = require('nanoid'); const { logger } = require('@librechat/data-schemas'); +const { Callback, ToolEndHandler, formatAgentMessages } = require('@librechat/agents'); const { EModelEndpoint, ResourceType, PermissionBits } = require('librechat-data-provider'); -const { - Callback, - ToolEndHandler, - formatAgentMessages, - ChatModelStreamHandler, -} = require('@librechat/agents'); const { writeSSE, createRun, @@ -29,9 +24,6 @@ const { const { loadAgentTools, loadToolsForExecution } = require('~/server/services/ToolService'); const { createToolEndCallback } = require('~/server/controllers/agents/callbacks'); const { findAccessibleResources } = require('~/server/services/PermissionService'); -const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens'); -const { getConvoFiles } = require('~/models/Conversation'); -const { getAgent, getAgents } = require('~/models/Agent'); const db = require('~/models'); /** @@ -134,7 +126,6 @@ const OpenAIChatCompletionController = async (req, res) => { const appConfig = req.config; const requestStartTime = Date.now(); - // Validate request const validation = validateRequest(req.body); if (isChatCompletionValidationFailure(validation)) { return sendErrorResponse(res, 400, validation.error); @@ -144,7 +135,7 @@ const OpenAIChatCompletionController = async (req, res) => { const agentId = request.model; // Look up the agent - const agent = await getAgent({ id: agentId }); + const agent = await db.getAgent({ id: agentId }); if (!agent) { return sendErrorResponse( res, @@ -155,20 +146,18 @@ const OpenAIChatCompletionController = async (req, res) => { ); } - // Generate IDs - const requestId = `chatcmpl-${nanoid()}`; - const conversationId = request.conversation_id ?? nanoid(); - const parentMessageId = request.parent_message_id ?? null; + const responseId = `chatcmpl-${nanoid()}`; const created = Math.floor(Date.now() / 1000); + /** @type {import('@librechat/api').OpenAIResponseContext} — key must be `requestId` to match the type used by createChunk/buildNonStreamingResponse */ const context = { created, - requestId, + requestId: responseId, model: agentId, }; logger.debug( - `[OpenAI API] Request ${requestId} started for agent ${agentId}, stream: ${request.stream}`, + `[OpenAI API] Response ${responseId} started for agent ${agentId}, stream: ${request.stream}`, ); // Set up abort controller @@ -183,6 +172,23 @@ const OpenAIChatCompletionController = async (req, res) => { }); try { + if (request.conversation_id != null) { + if (typeof request.conversation_id !== 'string') { + return sendErrorResponse( + res, + 400, + 'conversation_id must be a string', + 'invalid_request_error', + ); + } + if (!(await getConvo(req.user?.id, request.conversation_id))) { + return sendErrorResponse(res, 404, 'Conversation not found', 'invalid_request_error'); + } + } + + const conversationId = request.conversation_id ?? nanoid(); + const parentMessageId = request.parent_message_id ?? null; + // Build allowed providers set const allowedProviders = new Set( appConfig?.endpoints?.[EModelEndpoint.agents]?.allowedProviders, @@ -211,7 +217,7 @@ const OpenAIChatCompletionController = async (req, res) => { isInitialAgent: true, }, { - getConvoFiles, + getConvoFiles: db.getConvoFiles, getFiles: db.getFiles, getUserKey: db.getUserKey, getMessages: db.getMessages, @@ -270,6 +276,7 @@ const OpenAIChatCompletionController = async (req, res) => { toolRegistry: primaryConfig.toolRegistry, userMCPAuthMap: primaryConfig.userMCPAuthMap, tool_resources: primaryConfig.tool_resources, + actionsEnabled: primaryConfig.actionsEnabled, }); }, toolEndCallback, @@ -325,18 +332,8 @@ const OpenAIChatCompletionController = async (req, res) => { } }; - // Built-in handler for processing raw model stream chunks - const chatModelStreamHandler = new ChatModelStreamHandler(); - // Event handlers for OpenAI-compatible streaming const handlers = { - // Process raw model chunks and dispatch message/reasoning deltas - on_chat_model_stream: { - handle: async (event, data, metadata, graph) => { - await chatModelStreamHandler.handle(event, data, metadata, graph); - }, - }, - // Text content streaming on_message_delta: createHandler((data) => { const content = data?.delta?.content; @@ -465,11 +462,11 @@ const OpenAIChatCompletionController = async (req, res) => { agents: [primaryConfig], messages: formattedMessages, indexTokenCountMap, - runId: requestId, + runId: responseId, signal: abortController.signal, customHandlers: handlers, requestBody: { - messageId: requestId, + messageId: responseId, conversationId, }, user: { id: userId }, @@ -486,6 +483,10 @@ const OpenAIChatCompletionController = async (req, res) => { thread_id: conversationId, user_id: userId, user: createSafeUser(req.user), + requestBody: { + messageId: responseId, + conversationId, + }, ...(userMCPAuthMap != null && { userMCPAuthMap }), }, signal: abortController.signal, @@ -505,12 +506,18 @@ const OpenAIChatCompletionController = async (req, res) => { const balanceConfig = getBalanceConfig(appConfig); const transactionsConfig = getTransactionsConfig(appConfig); recordCollectedUsage( - { spendTokens, spendStructuredTokens }, + { + spendTokens: db.spendTokens, + spendStructuredTokens: db.spendStructuredTokens, + pricing: { getMultiplier: db.getMultiplier, getCacheMultiplier: db.getCacheMultiplier }, + bulkWriteOps: { insertMany: db.bulkInsertTransactions, updateBalance: db.updateBalance }, + }, { user: userId, conversationId, collectedUsage, context: 'message', + messageId: responseId, balance: balanceConfig, transactions: transactionsConfig, model: primaryConfig.model || agent.model_parameters?.model, @@ -524,7 +531,7 @@ const OpenAIChatCompletionController = async (req, res) => { if (isStreaming) { sendFinalChunk(handlerConfig); res.end(); - logger.debug(`[OpenAI API] Request ${requestId} completed in ${duration}ms (streaming)`); + logger.debug(`[OpenAI API] Response ${responseId} completed in ${duration}ms (streaming)`); // Wait for artifact processing after response ends (non-blocking) if (artifactPromises.length > 0) { @@ -563,7 +570,9 @@ const OpenAIChatCompletionController = async (req, res) => { usage, ); res.json(response); - logger.debug(`[OpenAI API] Request ${requestId} completed in ${duration}ms (non-streaming)`); + logger.debug( + `[OpenAI API] Response ${responseId} completed in ${duration}ms (non-streaming)`, + ); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'An error occurred'; @@ -577,7 +586,14 @@ const OpenAIChatCompletionController = async (req, res) => { writeSSE(res, '[DONE]'); res.end(); } else { - sendErrorResponse(res, 500, errorMessage, 'server_error'); + // Forward upstream provider status codes (e.g., Anthropic 400s) instead of masking as 500 + const statusCode = + typeof error?.status === 'number' && error.status >= 400 && error.status < 600 + ? error.status + : 500; + const errorType = + statusCode >= 400 && statusCode < 500 ? 'invalid_request_error' : 'server_error'; + sendErrorResponse(res, statusCode, errorMessage, errorType); } } }; @@ -607,7 +623,7 @@ const ListModelsController = async (req, res) => { // Get the accessible agents let agents = []; if (accessibleAgentIds.length > 0) { - agents = await getAgents({ _id: { $in: accessibleAgentIds } }); + agents = await db.getAgents({ _id: { $in: accessibleAgentIds } }); } const models = agents.map((agent) => ({ @@ -650,7 +666,7 @@ const GetModelController = async (req, res) => { return sendErrorResponse(res, 401, 'Authentication required', 'auth_error'); } - const agent = await getAgent({ id: model }); + const agent = await db.getAgent({ id: model }); if (!agent) { return sendErrorResponse( diff --git a/api/server/controllers/agents/recordCollectedUsage.spec.js b/api/server/controllers/agents/recordCollectedUsage.spec.js index 6904f2ed39..009c5b262c 100644 --- a/api/server/controllers/agents/recordCollectedUsage.spec.js +++ b/api/server/controllers/agents/recordCollectedUsage.spec.js @@ -2,21 +2,29 @@ * Tests for AgentClient.recordCollectedUsage * * This is a critical function that handles token spending for agent LLM calls. - * It must correctly handle: - * - Sequential execution (single agent with tool calls) - * - Parallel execution (multiple agents with independent inputs) - * - Cache token handling (OpenAI and Anthropic formats) + * The client now delegates to the TS recordCollectedUsage from @librechat/api, + * passing pricing and bulkWriteOps deps. */ const { EModelEndpoint } = require('librechat-data-provider'); -// Mock dependencies before requiring the module const mockSpendTokens = jest.fn().mockResolvedValue(); const mockSpendStructuredTokens = jest.fn().mockResolvedValue(); +const mockGetMultiplier = jest.fn().mockReturnValue(1); +const mockGetCacheMultiplier = jest.fn().mockReturnValue(null); +const mockUpdateBalance = jest.fn().mockResolvedValue({}); +const mockBulkInsertTransactions = jest.fn().mockResolvedValue(undefined); +const mockRecordCollectedUsage = jest + .fn() + .mockResolvedValue({ input_tokens: 100, output_tokens: 50 }); -jest.mock('~/models/spendTokens', () => ({ +jest.mock('~/models', () => ({ spendTokens: (...args) => mockSpendTokens(...args), spendStructuredTokens: (...args) => mockSpendStructuredTokens(...args), + getMultiplier: mockGetMultiplier, + getCacheMultiplier: mockGetCacheMultiplier, + updateBalance: mockUpdateBalance, + bulkInsertTransactions: mockBulkInsertTransactions, })); jest.mock('~/config', () => ({ @@ -39,6 +47,14 @@ jest.mock('@librechat/agents', () => ({ }), })); +jest.mock('@librechat/api', () => { + const actual = jest.requireActual('@librechat/api'); + return { + ...actual, + recordCollectedUsage: (...args) => mockRecordCollectedUsage(...args), + }; +}); + const AgentClient = require('./client'); describe('AgentClient - recordCollectedUsage', () => { @@ -74,31 +90,66 @@ describe('AgentClient - recordCollectedUsage', () => { }); describe('basic functionality', () => { - it('should return early if collectedUsage is empty', async () => { + it('should delegate to recordCollectedUsage with full deps', async () => { + const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' }]; + + await client.recordCollectedUsage({ + collectedUsage, + balance: { enabled: true }, + transactions: { enabled: true }, + }); + + expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1); + const [deps, params] = mockRecordCollectedUsage.mock.calls[0]; + + expect(deps).toHaveProperty('spendTokens'); + expect(deps).toHaveProperty('spendStructuredTokens'); + expect(deps).toHaveProperty('pricing'); + expect(deps.pricing).toHaveProperty('getMultiplier'); + expect(deps.pricing).toHaveProperty('getCacheMultiplier'); + expect(deps).toHaveProperty('bulkWriteOps'); + expect(deps.bulkWriteOps).toHaveProperty('insertMany'); + expect(deps.bulkWriteOps).toHaveProperty('updateBalance'); + + expect(params).toEqual( + expect.objectContaining({ + user: 'user-123', + conversationId: 'convo-123', + collectedUsage, + context: 'message', + balance: { enabled: true }, + transactions: { enabled: true }, + }), + ); + }); + + it('should not set this.usage if collectedUsage is empty (returns undefined)', async () => { + mockRecordCollectedUsage.mockResolvedValue(undefined); + await client.recordCollectedUsage({ collectedUsage: [], balance: { enabled: true }, transactions: { enabled: true }, }); - expect(mockSpendTokens).not.toHaveBeenCalled(); - expect(mockSpendStructuredTokens).not.toHaveBeenCalled(); expect(client.usage).toBeUndefined(); }); - it('should return early if collectedUsage is null', async () => { + it('should not set this.usage if collectedUsage is null (returns undefined)', async () => { + mockRecordCollectedUsage.mockResolvedValue(undefined); + await client.recordCollectedUsage({ collectedUsage: null, balance: { enabled: true }, transactions: { enabled: true }, }); - expect(mockSpendTokens).not.toHaveBeenCalled(); expect(client.usage).toBeUndefined(); }); - it('should handle single usage entry correctly', async () => { - const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' }]; + it('should set this.usage from recordCollectedUsage result', async () => { + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 200, output_tokens: 75 }); + const collectedUsage = [{ input_tokens: 200, output_tokens: 75, model: 'gpt-4' }]; await client.recordCollectedUsage({ collectedUsage, @@ -106,521 +157,122 @@ describe('AgentClient - recordCollectedUsage', () => { transactions: { enabled: true }, }); - expect(mockSpendTokens).toHaveBeenCalledTimes(1); - expect(mockSpendTokens).toHaveBeenCalledWith( - expect.objectContaining({ - conversationId: 'convo-123', - user: 'user-123', - model: 'gpt-4', - }), - { promptTokens: 100, completionTokens: 50 }, - ); - expect(client.usage.input_tokens).toBe(100); - expect(client.usage.output_tokens).toBe(50); - }); - - it('should skip null entries in collectedUsage', async () => { - const collectedUsage = [ - { input_tokens: 100, output_tokens: 50, model: 'gpt-4' }, - null, - { input_tokens: 200, output_tokens: 60, model: 'gpt-4' }, - ]; - - await client.recordCollectedUsage({ - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - expect(mockSpendTokens).toHaveBeenCalledTimes(2); + expect(client.usage).toEqual({ input_tokens: 200, output_tokens: 75 }); }); }); describe('sequential execution (single agent with tool calls)', () => { - it('should calculate tokens correctly for sequential tool calls', async () => { - // Sequential flow: output of call N becomes part of input for call N+1 - // Call 1: input=100, output=50 - // Call 2: input=150 (100+50), output=30 - // Call 3: input=180 (150+30), output=20 + it('should pass all usage entries to recordCollectedUsage', async () => { const collectedUsage = [ { input_tokens: 100, output_tokens: 50, model: 'gpt-4' }, { input_tokens: 150, output_tokens: 30, model: 'gpt-4' }, { input_tokens: 180, output_tokens: 20, model: 'gpt-4' }, ]; + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 100 }); + await client.recordCollectedUsage({ collectedUsage, balance: { enabled: true }, transactions: { enabled: true }, }); - expect(mockSpendTokens).toHaveBeenCalledTimes(3); - // Total output should be sum of all output_tokens: 50 + 30 + 20 = 100 + expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1); + const [, params] = mockRecordCollectedUsage.mock.calls[0]; + expect(params.collectedUsage).toHaveLength(3); expect(client.usage.output_tokens).toBe(100); - expect(client.usage.input_tokens).toBe(100); // First entry's input + expect(client.usage.input_tokens).toBe(100); }); }); describe('parallel execution (multiple agents)', () => { - it('should handle parallel agents with independent input tokens', async () => { - // Parallel agents have INDEPENDENT input tokens (not cumulative) - // Agent A: input=100, output=50 - // Agent B: input=80, output=40 (different context, not 100+50) + it('should pass parallel agent usage to recordCollectedUsage', async () => { const collectedUsage = [ { input_tokens: 100, output_tokens: 50, model: 'gpt-4' }, { input_tokens: 80, output_tokens: 40, model: 'gpt-4' }, ]; + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 90 }); + await client.recordCollectedUsage({ collectedUsage, balance: { enabled: true }, transactions: { enabled: true }, }); - expect(mockSpendTokens).toHaveBeenCalledTimes(2); - // Expected total output: 50 + 40 = 90 - // output_tokens must be positive and should reflect total output + expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1); + expect(client.usage.output_tokens).toBe(90); expect(client.usage.output_tokens).toBeGreaterThan(0); }); - it('should NOT produce negative output_tokens for parallel execution', async () => { - // Critical bug scenario: parallel agents where second agent has LOWER input tokens + /** Bug regression: parallel agents where second agent has LOWER input tokens produced negative output via incremental calculation. */ + it('should NOT produce negative output_tokens', async () => { const collectedUsage = [ { input_tokens: 200, output_tokens: 100, model: 'gpt-4' }, { input_tokens: 50, output_tokens: 30, model: 'gpt-4' }, ]; + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 200, output_tokens: 130 }); + await client.recordCollectedUsage({ collectedUsage, balance: { enabled: true }, transactions: { enabled: true }, }); - // output_tokens MUST be positive for proper token tracking expect(client.usage.output_tokens).toBeGreaterThan(0); - // Correct value should be 100 + 30 = 130 - }); - - it('should calculate correct total output for parallel agents', async () => { - // Three parallel agents with independent contexts - const collectedUsage = [ - { input_tokens: 100, output_tokens: 50, model: 'gpt-4' }, - { input_tokens: 120, output_tokens: 60, model: 'gpt-4-turbo' }, - { input_tokens: 80, output_tokens: 40, model: 'claude-3' }, - ]; - - await client.recordCollectedUsage({ - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - expect(mockSpendTokens).toHaveBeenCalledTimes(3); - // Total output should be 50 + 60 + 40 = 150 - expect(client.usage.output_tokens).toBe(150); - }); - - it('should handle worst-case parallel scenario without negative tokens', async () => { - // Extreme case: first agent has very high input, subsequent have low - const collectedUsage = [ - { input_tokens: 1000, output_tokens: 500, model: 'gpt-4' }, - { input_tokens: 100, output_tokens: 50, model: 'gpt-4' }, - { input_tokens: 50, output_tokens: 25, model: 'gpt-4' }, - ]; - - await client.recordCollectedUsage({ - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - // Must be positive, should be 500 + 50 + 25 = 575 - expect(client.usage.output_tokens).toBeGreaterThan(0); - expect(client.usage.output_tokens).toBe(575); + expect(client.usage.output_tokens).toBe(130); }); }); describe('real-world scenarios', () => { - it('should correctly sum output tokens for sequential tool calls with growing context', async () => { - // Real production data: Claude Opus with multiple tool calls - // Context grows as tool results are added, but output_tokens should only count model generations + it('should correctly handle sequential tool calls with growing context', async () => { const collectedUsage = [ - { - input_tokens: 31596, - output_tokens: 151, - total_tokens: 31747, - input_token_details: { cache_read: 0, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 35368, - output_tokens: 150, - total_tokens: 35518, - input_token_details: { cache_read: 0, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 58362, - output_tokens: 295, - total_tokens: 58657, - input_token_details: { cache_read: 0, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 112604, - output_tokens: 193, - total_tokens: 112797, - input_token_details: { cache_read: 0, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 257440, - output_tokens: 2217, - total_tokens: 259657, - input_token_details: { cache_read: 0, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, + { input_tokens: 31596, output_tokens: 151, model: 'claude-opus-4-5-20251101' }, + { input_tokens: 35368, output_tokens: 150, model: 'claude-opus-4-5-20251101' }, + { input_tokens: 58362, output_tokens: 295, model: 'claude-opus-4-5-20251101' }, + { input_tokens: 112604, output_tokens: 193, model: 'claude-opus-4-5-20251101' }, + { input_tokens: 257440, output_tokens: 2217, model: 'claude-opus-4-5-20251101' }, ]; + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 31596, output_tokens: 3006 }); + await client.recordCollectedUsage({ collectedUsage, balance: { enabled: true }, transactions: { enabled: true }, }); - // input_tokens should be first entry's input (initial context) expect(client.usage.input_tokens).toBe(31596); - - // output_tokens should be sum of all model outputs: 151 + 150 + 295 + 193 + 2217 = 3006 - // NOT the inflated value from incremental calculation (338,559) expect(client.usage.output_tokens).toBe(3006); - - // Verify spendTokens was called for each entry with correct values - expect(mockSpendTokens).toHaveBeenCalledTimes(5); - expect(mockSpendTokens).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ model: 'claude-opus-4-5-20251101' }), - { promptTokens: 31596, completionTokens: 151 }, - ); - expect(mockSpendTokens).toHaveBeenNthCalledWith( - 5, - expect.objectContaining({ model: 'claude-opus-4-5-20251101' }), - { promptTokens: 257440, completionTokens: 2217 }, - ); }); - it('should handle single followup message correctly', async () => { - // Real production data: followup to the above conversation - const collectedUsage = [ - { - input_tokens: 263406, - output_tokens: 257, - total_tokens: 263663, - input_token_details: { cache_read: 0, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - ]; - - await client.recordCollectedUsage({ - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - expect(client.usage.input_tokens).toBe(263406); - expect(client.usage.output_tokens).toBe(257); - - expect(mockSpendTokens).toHaveBeenCalledTimes(1); - expect(mockSpendTokens).toHaveBeenCalledWith( - expect.objectContaining({ model: 'claude-opus-4-5-20251101' }), - { promptTokens: 263406, completionTokens: 257 }, - ); - }); - - it('should ensure output_tokens > 0 check passes for BaseClient.sendMessage', async () => { - // This verifies the fix for the duplicate token spending bug - // BaseClient.sendMessage checks: if (usage != null && Number(usage[this.outputTokensKey]) > 0) - const collectedUsage = [ - { - input_tokens: 31596, - output_tokens: 151, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 35368, - output_tokens: 150, - model: 'claude-opus-4-5-20251101', - }, - ]; - - await client.recordCollectedUsage({ - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - const usage = client.getStreamUsage(); - - // The check that was failing before the fix - expect(usage).not.toBeNull(); - expect(Number(usage.output_tokens)).toBeGreaterThan(0); - - // Verify correct value - expect(usage.output_tokens).toBe(301); // 151 + 150 - }); - - it('should correctly handle cache tokens with multiple tool calls', async () => { - // Real production data: Claude Opus with cache tokens (prompt caching) - // First entry has cache_creation, subsequent entries have cache_read + it('should correctly handle cache tokens', async () => { const collectedUsage = [ { input_tokens: 788, output_tokens: 163, - total_tokens: 951, input_token_details: { cache_read: 0, cache_creation: 30808 }, model: 'claude-opus-4-5-20251101', }, - { - input_tokens: 3802, - output_tokens: 149, - total_tokens: 3951, - input_token_details: { cache_read: 30808, cache_creation: 768 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 26808, - output_tokens: 225, - total_tokens: 27033, - input_token_details: { cache_read: 31576, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 80912, - output_tokens: 204, - total_tokens: 81116, - input_token_details: { cache_read: 31576, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 136454, - output_tokens: 206, - total_tokens: 136660, - input_token_details: { cache_read: 31576, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 146316, - output_tokens: 224, - total_tokens: 146540, - input_token_details: { cache_read: 31576, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 150402, - output_tokens: 1248, - total_tokens: 151650, - input_token_details: { cache_read: 31576, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 156268, - output_tokens: 139, - total_tokens: 156407, - input_token_details: { cache_read: 31576, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, - { - input_tokens: 167126, - output_tokens: 2961, - total_tokens: 170087, - input_token_details: { cache_read: 31576, cache_creation: 0 }, - model: 'claude-opus-4-5-20251101', - }, ]; + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 31596, output_tokens: 163 }); + await client.recordCollectedUsage({ collectedUsage, balance: { enabled: true }, transactions: { enabled: true }, }); - // input_tokens = first entry's input + cache_creation + cache_read - // = 788 + 30808 + 0 = 31596 expect(client.usage.input_tokens).toBe(31596); - - // output_tokens = sum of all output_tokens - // = 163 + 149 + 225 + 204 + 206 + 224 + 1248 + 139 + 2961 = 5519 - expect(client.usage.output_tokens).toBe(5519); - - // First 2 entries have cache tokens, should use spendStructuredTokens - // Remaining 7 entries have cache_read but no cache_creation, still structured - expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(9); - expect(mockSpendTokens).toHaveBeenCalledTimes(0); - - // Verify first entry uses structured tokens with cache_creation - expect(mockSpendStructuredTokens).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ model: 'claude-opus-4-5-20251101' }), - { - promptTokens: { input: 788, write: 30808, read: 0 }, - completionTokens: 163, - }, - ); - - // Verify second entry uses structured tokens with both cache_creation and cache_read - expect(mockSpendStructuredTokens).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ model: 'claude-opus-4-5-20251101' }), - { - promptTokens: { input: 3802, write: 768, read: 30808 }, - completionTokens: 149, - }, - ); - }); - }); - - describe('cache token handling', () => { - it('should handle OpenAI format cache tokens (input_token_details)', async () => { - const collectedUsage = [ - { - input_tokens: 100, - output_tokens: 50, - model: 'gpt-4', - input_token_details: { - cache_creation: 20, - cache_read: 10, - }, - }, - ]; - - await client.recordCollectedUsage({ - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1); - expect(mockSpendStructuredTokens).toHaveBeenCalledWith( - expect.objectContaining({ model: 'gpt-4' }), - { - promptTokens: { - input: 100, - write: 20, - read: 10, - }, - completionTokens: 50, - }, - ); - }); - - it('should handle Anthropic format cache tokens (cache_*_input_tokens)', async () => { - const collectedUsage = [ - { - input_tokens: 100, - output_tokens: 50, - model: 'claude-3', - cache_creation_input_tokens: 25, - cache_read_input_tokens: 15, - }, - ]; - - await client.recordCollectedUsage({ - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1); - expect(mockSpendStructuredTokens).toHaveBeenCalledWith( - expect.objectContaining({ model: 'claude-3' }), - { - promptTokens: { - input: 100, - write: 25, - read: 15, - }, - completionTokens: 50, - }, - ); - }); - - it('should use spendTokens for entries without cache tokens', async () => { - const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' }]; - - await client.recordCollectedUsage({ - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - expect(mockSpendTokens).toHaveBeenCalledTimes(1); - expect(mockSpendStructuredTokens).not.toHaveBeenCalled(); - }); - - it('should handle mixed cache and non-cache entries', async () => { - const collectedUsage = [ - { input_tokens: 100, output_tokens: 50, model: 'gpt-4' }, - { - input_tokens: 150, - output_tokens: 30, - model: 'gpt-4', - input_token_details: { cache_creation: 10, cache_read: 5 }, - }, - { input_tokens: 200, output_tokens: 20, model: 'gpt-4' }, - ]; - - await client.recordCollectedUsage({ - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - expect(mockSpendTokens).toHaveBeenCalledTimes(2); - expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1); - }); - - it('should include cache tokens in total input calculation', async () => { - const collectedUsage = [ - { - input_tokens: 100, - output_tokens: 50, - model: 'gpt-4', - input_token_details: { - cache_creation: 20, - cache_read: 10, - }, - }, - ]; - - await client.recordCollectedUsage({ - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - // Total input should include cache tokens: 100 + 20 + 10 = 130 - expect(client.usage.input_tokens).toBe(130); + expect(client.usage.output_tokens).toBe(163); }); }); describe('model fallback', () => { - it('should use usage.model when available', async () => { - const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4-turbo' }]; - - await client.recordCollectedUsage({ - model: 'fallback-model', - collectedUsage, - balance: { enabled: true }, - transactions: { enabled: true }, - }); - - expect(mockSpendTokens).toHaveBeenCalledWith( - expect.objectContaining({ model: 'gpt-4-turbo' }), - expect.any(Object), - ); - }); - - it('should fallback to param model when usage.model is missing', async () => { + it('should use param model when available', async () => { + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 50 }); const collectedUsage = [{ input_tokens: 100, output_tokens: 50 }]; await client.recordCollectedUsage({ @@ -630,14 +282,13 @@ describe('AgentClient - recordCollectedUsage', () => { transactions: { enabled: true }, }); - expect(mockSpendTokens).toHaveBeenCalledWith( - expect.objectContaining({ model: 'param-model' }), - expect.any(Object), - ); + const [, params] = mockRecordCollectedUsage.mock.calls[0]; + expect(params.model).toBe('param-model'); }); it('should fallback to client.model when param model is missing', async () => { client.model = 'client-model'; + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 50 }); const collectedUsage = [{ input_tokens: 100, output_tokens: 50 }]; await client.recordCollectedUsage({ @@ -646,13 +297,12 @@ describe('AgentClient - recordCollectedUsage', () => { transactions: { enabled: true }, }); - expect(mockSpendTokens).toHaveBeenCalledWith( - expect.objectContaining({ model: 'client-model' }), - expect.any(Object), - ); + const [, params] = mockRecordCollectedUsage.mock.calls[0]; + expect(params.model).toBe('client-model'); }); it('should fallback to agent model_parameters.model as last resort', async () => { + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 50 }); const collectedUsage = [{ input_tokens: 100, output_tokens: 50 }]; await client.recordCollectedUsage({ @@ -661,15 +311,14 @@ describe('AgentClient - recordCollectedUsage', () => { transactions: { enabled: true }, }); - expect(mockSpendTokens).toHaveBeenCalledWith( - expect.objectContaining({ model: 'gpt-4' }), - expect.any(Object), - ); + const [, params] = mockRecordCollectedUsage.mock.calls[0]; + expect(params.model).toBe('gpt-4'); }); }); describe('getStreamUsage integration', () => { it('should return the usage object set by recordCollectedUsage', async () => { + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 100, output_tokens: 50 }); const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' }]; await client.recordCollectedUsage({ @@ -679,10 +328,7 @@ describe('AgentClient - recordCollectedUsage', () => { }); const usage = client.getStreamUsage(); - expect(usage).toEqual({ - input_tokens: 100, - output_tokens: 50, - }); + expect(usage).toEqual({ input_tokens: 100, output_tokens: 50 }); }); it('should return undefined before recordCollectedUsage is called', () => { @@ -690,9 +336,9 @@ describe('AgentClient - recordCollectedUsage', () => { expect(usage).toBeUndefined(); }); + /** Verifies usage passes the check in BaseClient.sendMessage: if (usage != null && Number(usage[this.outputTokensKey]) > 0) */ it('should have output_tokens > 0 for BaseClient.sendMessage check', async () => { - // This test verifies the usage will pass the check in BaseClient.sendMessage: - // if (usage != null && Number(usage[this.outputTokensKey]) > 0) + mockRecordCollectedUsage.mockResolvedValue({ input_tokens: 200, output_tokens: 130 }); const collectedUsage = [ { input_tokens: 200, output_tokens: 100, model: 'gpt-4' }, { input_tokens: 50, output_tokens: 30, model: 'gpt-4' }, diff --git a/api/server/controllers/agents/request.js b/api/server/controllers/agents/request.js index 79387b6e89..6f7e1b88c1 100644 --- a/api/server/controllers/agents/request.js +++ b/api/server/controllers/agents/request.js @@ -3,9 +3,9 @@ const { Constants, ViolationTypes } = require('librechat-data-provider'); const { sendEvent, getViolationInfo, + buildMessageFiles, GenerationJobManager, decrementPendingRequest, - sanitizeFileForTransmit, sanitizeMessageForTransmit, checkAndIncrementPendingRequest, } = require('@librechat/api'); @@ -131,9 +131,15 @@ const ResumableAgentController = async (req, res, next, initializeClient, addTit partialMessage.agent_id = req.body.agent_id; } - await saveMessage(req, partialMessage, { - context: 'api/server/controllers/agents/request.js - partial response on disconnect', - }); + await saveMessage( + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, + partialMessage, + { context: 'api/server/controllers/agents/request.js - partial response on disconnect' }, + ); logger.debug( `[ResumableAgentController] Saved partial response for ${streamId}, content parts: ${aggregatedContent.length}`, @@ -252,13 +258,10 @@ const ResumableAgentController = async (req, res, next, initializeClient, addTit conversation.title = conversation && !conversation.title ? null : conversation?.title || 'New Chat'; - if (req.body.files && client.options?.attachments) { - userMessage.files = []; - const messageFiles = new Set(req.body.files.map((file) => file.file_id)); - for (const attachment of client.options.attachments) { - if (messageFiles.has(attachment.file_id)) { - userMessage.files.push(sanitizeFileForTransmit(attachment)); - } + if (req.body.files && Array.isArray(client.options.attachments)) { + const files = buildMessageFiles(req.body.files, client.options.attachments); + if (files.length > 0) { + userMessage.files = files; } delete userMessage.image_urls; } @@ -274,8 +277,14 @@ const ResumableAgentController = async (req, res, next, initializeClient, addTit // Save user message BEFORE sending final event to avoid race condition // where client refetch happens before database is updated + const reqCtx = { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }; + if (!client.skipSaveUserMessage && userMessage) { - await saveMessage(req, userMessage, { + await saveMessage(reqCtx, userMessage, { context: 'api/server/controllers/agents/request.js - resumable user message', }); } @@ -285,7 +294,7 @@ const ResumableAgentController = async (req, res, next, initializeClient, addTit // before the response is saved to the database, causing orphaned parentMessageIds. if (client.savedMessageIds && !client.savedMessageIds.has(messageId)) { await saveMessage( - req, + reqCtx, { ...response, user: userId, unfinished: wasAbortedBeforeComplete }, { context: 'api/server/controllers/agents/request.js - resumable response end' }, ); @@ -639,14 +648,10 @@ const _LegacyAgentController = async (req, res, next, initializeClient, addTitle conversation.title = conversation && !conversation.title ? null : conversation?.title || 'New Chat'; - // Process files if needed (sanitize to remove large text fields before transmission) - if (req.body.files && client.options?.attachments) { - userMessage.files = []; - const messageFiles = new Set(req.body.files.map((file) => file.file_id)); - for (const attachment of client.options.attachments) { - if (messageFiles.has(attachment.file_id)) { - userMessage.files.push(sanitizeFileForTransmit(attachment)); - } + if (req.body.files && Array.isArray(client.options.attachments)) { + const files = buildMessageFiles(req.body.files, client.options.attachments); + if (files.length > 0) { + userMessage.files = files; } delete userMessage.image_urls; } @@ -668,7 +673,11 @@ const _LegacyAgentController = async (req, res, next, initializeClient, addTitle // Save the message if needed if (client.savedMessageIds && !client.savedMessageIds.has(messageId)) { await saveMessage( - req, + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { ...finalResponse, user: userId }, { context: 'api/server/controllers/agents/request.js - response end' }, ); @@ -697,9 +706,15 @@ const _LegacyAgentController = async (req, res, next, initializeClient, addTitle // Save user message if needed if (!client.skipSaveUserMessage) { - await saveMessage(req, userMessage, { - context: "api/server/controllers/agents/request.js - don't skip saving user message", - }); + await saveMessage( + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, + userMessage, + { context: "api/server/controllers/agents/request.js - don't skip saving user message" }, + ); } // Add title if needed - extract minimal data diff --git a/api/server/controllers/agents/responses.js b/api/server/controllers/agents/responses.js index 3cd1dff5eb..62cedb14fd 100644 --- a/api/server/controllers/agents/responses.js +++ b/api/server/controllers/agents/responses.js @@ -1,13 +1,8 @@ const { nanoid } = require('nanoid'); const { v4: uuidv4 } = require('uuid'); const { logger } = require('@librechat/data-schemas'); +const { Callback, ToolEndHandler, formatAgentMessages } = require('@librechat/agents'); const { EModelEndpoint, ResourceType, PermissionBits } = require('librechat-data-provider'); -const { - Callback, - ToolEndHandler, - formatAgentMessages, - ChatModelStreamHandler, -} = require('@librechat/agents'); const { createRun, buildToolSet, @@ -41,9 +36,6 @@ const { } = require('~/server/controllers/agents/callbacks'); const { loadAgentTools, loadToolsForExecution } = require('~/server/services/ToolService'); const { findAccessibleResources } = require('~/server/services/PermissionService'); -const { getConvoFiles, saveConvo, getConvo } = require('~/models/Conversation'); -const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens'); -const { getAgent, getAgents } = require('~/models/Agent'); const db = require('~/models'); /** @type {import('@librechat/api').AppConfig | null} */ @@ -218,8 +210,12 @@ async function saveResponseOutput(req, conversationId, responseId, response, age * @returns {Promise} */ async function saveConversation(req, conversationId, agentId, agent) { - await saveConvo( - req, + await db.saveConvo( + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { conversationId, endpoint: EModelEndpoint.agents, @@ -283,7 +279,7 @@ const createResponse = async (req, res) => { const isStreaming = request.stream === true; // Look up the agent - const agent = await getAgent({ id: agentId }); + const agent = await db.getAgent({ id: agentId }); if (!agent) { return sendResponsesErrorResponse( res, @@ -296,10 +292,6 @@ const createResponse = async (req, res) => { // Generate IDs const responseId = generateResponseId(); - const conversationId = request.previous_response_id ?? uuidv4(); - const parentMessageId = null; - - // Create response context const context = createResponseContext(request, responseId); logger.debug( @@ -318,6 +310,23 @@ const createResponse = async (req, res) => { }); try { + if (request.previous_response_id != null) { + if (typeof request.previous_response_id !== 'string') { + return sendResponsesErrorResponse( + res, + 400, + 'previous_response_id must be a string', + 'invalid_request', + ); + } + if (!(await getConvo(req.user?.id, request.previous_response_id))) { + return sendResponsesErrorResponse(res, 404, 'Conversation not found', 'not_found'); + } + } + + const conversationId = request.previous_response_id ?? uuidv4(); + const parentMessageId = null; + // Build allowed providers set const allowedProviders = new Set( appConfig?.endpoints?.[EModelEndpoint.agents]?.allowedProviders, @@ -346,7 +355,7 @@ const createResponse = async (req, res) => { isInitialAgent: true, }, { - getConvoFiles, + getConvoFiles: db.getConvoFiles, getFiles: db.getFiles, getUserKey: db.getUserKey, getMessages: db.getMessages, @@ -410,9 +419,6 @@ const createResponse = async (req, res) => { // Collect usage for balance tracking const collectedUsage = []; - // Built-in handler for processing raw model stream chunks - const chatModelStreamHandler = new ChatModelStreamHandler(); - // Artifact promises for processing tool outputs /** @type {Promise[]} */ const artifactPromises = []; @@ -436,6 +442,7 @@ const createResponse = async (req, res) => { toolRegistry: primaryConfig.toolRegistry, userMCPAuthMap: primaryConfig.userMCPAuthMap, tool_resources: primaryConfig.tool_resources, + actionsEnabled: primaryConfig.actionsEnabled, }); }, toolEndCallback, @@ -443,11 +450,6 @@ const createResponse = async (req, res) => { // Combine handlers const handlers = { - on_chat_model_stream: { - handle: async (event, data, metadata, graph) => { - await chatModelStreamHandler.handle(event, data, metadata, graph); - }, - }, on_message_delta: responsesHandlers.on_message_delta, on_reasoning_delta: responsesHandlers.on_reasoning_delta, on_run_step: responsesHandlers.on_run_step, @@ -499,6 +501,10 @@ const createResponse = async (req, res) => { thread_id: conversationId, user_id: userId, user: createSafeUser(req.user), + requestBody: { + messageId: responseId, + conversationId, + }, ...(userMCPAuthMap != null && { userMCPAuthMap }), }, signal: abortController.signal, @@ -518,12 +524,18 @@ const createResponse = async (req, res) => { const balanceConfig = getBalanceConfig(req.config); const transactionsConfig = getTransactionsConfig(req.config); recordCollectedUsage( - { spendTokens, spendStructuredTokens }, + { + spendTokens: db.spendTokens, + spendStructuredTokens: db.spendStructuredTokens, + pricing: { getMultiplier: db.getMultiplier, getCacheMultiplier: db.getCacheMultiplier }, + bulkWriteOps: { insertMany: db.bulkInsertTransactions, updateBalance: db.updateBalance }, + }, { user: userId, conversationId, collectedUsage, context: 'message', + messageId: responseId, balance: balanceConfig, transactions: transactionsConfig, model: primaryConfig.model || agent.model_parameters?.model, @@ -570,8 +582,6 @@ const createResponse = async (req, res) => { } else { const aggregatorHandlers = createAggregatorEventHandlers(aggregator); - const chatModelStreamHandler = new ChatModelStreamHandler(); - // Collect usage for balance tracking const collectedUsage = []; @@ -590,17 +600,13 @@ const createResponse = async (req, res) => { toolRegistry: primaryConfig.toolRegistry, userMCPAuthMap: primaryConfig.userMCPAuthMap, tool_resources: primaryConfig.tool_resources, + actionsEnabled: primaryConfig.actionsEnabled, }); }, toolEndCallback, }; const handlers = { - on_chat_model_stream: { - handle: async (event, data, metadata, graph) => { - await chatModelStreamHandler.handle(event, data, metadata, graph); - }, - }, on_message_delta: aggregatorHandlers.on_message_delta, on_reasoning_delta: aggregatorHandlers.on_reasoning_delta, on_run_step: aggregatorHandlers.on_run_step, @@ -650,6 +656,10 @@ const createResponse = async (req, res) => { thread_id: conversationId, user_id: userId, user: createSafeUser(req.user), + requestBody: { + messageId: responseId, + conversationId, + }, ...(userMCPAuthMap != null && { userMCPAuthMap }), }, signal: abortController.signal, @@ -669,12 +679,18 @@ const createResponse = async (req, res) => { const balanceConfig = getBalanceConfig(req.config); const transactionsConfig = getTransactionsConfig(req.config); recordCollectedUsage( - { spendTokens, spendStructuredTokens }, + { + spendTokens: db.spendTokens, + spendStructuredTokens: db.spendStructuredTokens, + pricing: { getMultiplier: db.getMultiplier, getCacheMultiplier: db.getCacheMultiplier }, + bulkWriteOps: { insertMany: db.bulkInsertTransactions, updateBalance: db.updateBalance }, + }, { user: userId, conversationId, collectedUsage, context: 'message', + messageId: responseId, balance: balanceConfig, transactions: transactionsConfig, model: primaryConfig.model || agent.model_parameters?.model, @@ -727,7 +743,13 @@ const createResponse = async (req, res) => { writeDone(res); res.end(); } else { - sendResponsesErrorResponse(res, 500, errorMessage, 'server_error'); + // Forward upstream provider status codes (e.g., Anthropic 400s) instead of masking as 500 + const statusCode = + typeof error?.status === 'number' && error.status >= 400 && error.status < 600 + ? error.status + : 500; + const errorType = statusCode >= 400 && statusCode < 500 ? 'invalid_request' : 'server_error'; + sendResponsesErrorResponse(res, statusCode, errorMessage, errorType); } } }; @@ -760,7 +782,7 @@ const listModels = async (req, res) => { // Get the accessible agents let agents = []; if (accessibleAgentIds.length > 0) { - agents = await getAgents({ _id: { $in: accessibleAgentIds } }); + agents = await db.getAgents({ _id: { $in: accessibleAgentIds } }); } // Convert to models format @@ -810,7 +832,7 @@ const getResponse = async (req, res) => { // The responseId could be either the response ID or the conversation ID // Try to find a conversation with this ID - const conversation = await getConvo(userId, responseId); + const conversation = await db.getConvo(userId, responseId); if (!conversation) { return sendResponsesErrorResponse( diff --git a/api/server/controllers/agents/v1.js b/api/server/controllers/agents/v1.js index 34078b2250..17985f97ce 100644 --- a/api/server/controllers/agents/v1.js +++ b/api/server/controllers/agents/v1.js @@ -3,9 +3,11 @@ const fs = require('fs').promises; const { nanoid } = require('nanoid'); const { logger } = require('@librechat/data-schemas'); const { + refreshS3Url, agentCreateSchema, agentUpdateSchema, refreshListAvatars, + collectEdgeAgentIds, mergeAgentOcrConversion, MAX_AVATAR_REFRESH_AGENTS, convertOcrToContextInPlace, @@ -24,30 +26,21 @@ const { actionDelimiter, removeNullishValues, } = require('librechat-data-provider'); -const { - getListAgentsByAccess, - countPromotedAgents, - revertAgentVersion, - createAgent, - updateAgent, - deleteAgent, - getAgent, -} = require('~/models/Agent'); const { findPubliclyAccessibleResources, + getResourcePermissionsMap, findAccessibleResources, hasPublicPermission, grantPermission, } = require('~/server/services/PermissionService'); const { getStrategyFunctions } = require('~/server/services/Files/strategies'); -const { getCategoriesWithCounts, deleteFileByFilter } = require('~/models'); const { resizeAvatar } = require('~/server/services/Files/images/avatar'); const { getFileStrategy } = require('~/server/utils/getFileStrategy'); -const { refreshS3Url } = require('~/server/services/Files/S3/crud'); const { filterFile } = require('~/server/services/Files/process'); -const { updateAction, getActions } = require('~/models/Action'); const { getCachedTools } = require('~/server/services/Config'); +const { getMCPServersRegistry } = require('~/config'); const { getLogStores } = require('~/cache'); +const db = require('~/models'); const systemTools = { [Tools.execute_code]: true, @@ -58,6 +51,114 @@ const systemTools = { const MAX_SEARCH_LEN = 100; const escapeRegex = (str = '') => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +/** + * Validates that the requesting user has VIEW access to every agent referenced in edges. + * Agents that do not exist in the database are skipped — at create time, the `from` field + * often references the agent being built, which has no DB record yet. + * @param {import('librechat-data-provider').GraphEdge[]} edges + * @param {string} userId + * @param {string} userRole - Used for group/role principal resolution + * @returns {Promise} Agent IDs the user cannot VIEW (empty if all accessible) + */ +const validateEdgeAgentAccess = async (edges, userId, userRole) => { + const edgeAgentIds = collectEdgeAgentIds(edges); + if (edgeAgentIds.size === 0) { + return []; + } + + const agents = await db.getAgents({ id: { $in: [...edgeAgentIds] } }); + + if (agents.length === 0) { + return []; + } + + const permissionsMap = await getResourcePermissionsMap({ + userId, + role: userRole, + resourceType: ResourceType.AGENT, + resourceIds: agents.map((a) => a._id), + }); + + return agents + .filter((a) => { + const bits = permissionsMap.get(a._id.toString()) ?? 0; + return (bits & PermissionBits.VIEW) === 0; + }) + .map((a) => a.id); +}; + +/** + * Filters tools to only include those the user is authorized to use. + * MCP tools must match the exact format `{toolName}_mcp_{serverName}` (exactly 2 segments). + * Multi-delimiter keys are rejected to prevent authorization/execution mismatch. + * Non-MCP tools must appear in availableTools (global tool cache) or systemTools. + * + * When `existingTools` is provided and the MCP registry is unavailable (e.g. server restart), + * tools already present on the agent are preserved rather than stripped — they were validated + * when originally added, and we cannot re-verify them without the registry. + * @param {object} params + * @param {string[]} params.tools - Raw tool strings from the request + * @param {string} params.userId - Requesting user ID for MCP server access check + * @param {Record} params.availableTools - Global non-MCP tool cache + * @param {string[]} [params.existingTools] - Tools already persisted on the agent document + * @returns {Promise} Only the authorized subset of tools + */ +const filterAuthorizedTools = async ({ tools, userId, availableTools, existingTools }) => { + const filteredTools = []; + let mcpServerConfigs; + let registryUnavailable = false; + const existingToolSet = existingTools?.length ? new Set(existingTools) : null; + + for (const tool of tools) { + if (availableTools[tool] || systemTools[tool]) { + filteredTools.push(tool); + continue; + } + + if (!tool?.includes(Constants.mcp_delimiter)) { + continue; + } + + if (mcpServerConfigs === undefined) { + try { + mcpServerConfigs = (await getMCPServersRegistry().getAllServerConfigs(userId)) ?? {}; + } catch (e) { + logger.warn( + '[filterAuthorizedTools] MCP registry unavailable, filtering all MCP tools', + e.message, + ); + mcpServerConfigs = {}; + registryUnavailable = true; + } + } + + const parts = tool.split(Constants.mcp_delimiter); + if (parts.length !== 2) { + logger.warn( + `[filterAuthorizedTools] Rejected malformed MCP tool key "${tool}" for user ${userId}`, + ); + continue; + } + + if (registryUnavailable && existingToolSet?.has(tool)) { + filteredTools.push(tool); + continue; + } + + const [, serverName] = parts; + if (!serverName || !Object.hasOwn(mcpServerConfigs, serverName)) { + logger.warn( + `[filterAuthorizedTools] Rejected MCP tool "${tool}" — server "${serverName}" not accessible to user ${userId}`, + ); + continue; + } + + filteredTools.push(tool); + } + + return filteredTools; +}; + /** * Creates an Agent. * @route POST /Agents @@ -75,24 +176,26 @@ const createAgentHandler = async (req, res) => { agentData.model_parameters = removeNullishValues(agentData.model_parameters, true); } - const { id: userId } = req.user; + const { id: userId, role: userRole } = req.user; + + if (agentData.edges?.length) { + const unauthorized = await validateEdgeAgentAccess(agentData.edges, userId, userRole); + if (unauthorized.length > 0) { + return res.status(403).json({ + error: 'You do not have access to one or more agents referenced in edges', + agent_ids: unauthorized, + }); + } + } agentData.id = `agent_${nanoid()}`; agentData.author = userId; agentData.tools = []; const availableTools = (await getCachedTools()) ?? {}; - for (const tool of tools) { - if (availableTools[tool]) { - agentData.tools.push(tool); - } else if (systemTools[tool]) { - agentData.tools.push(tool); - } else if (tool.includes(Constants.mcp_delimiter)) { - agentData.tools.push(tool); - } - } + agentData.tools = await filterAuthorizedTools({ tools, userId, availableTools }); - const agent = await createAgent(agentData); + const agent = await db.createAgent(agentData); try { await Promise.all([ @@ -152,7 +255,7 @@ const getAgentHandler = async (req, res, expandProperties = false) => { // Permissions are validated by middleware before calling this function // Simply load the agent by ID - const agent = await getAgent({ id }); + const agent = await db.getAgent({ id }); if (!agent) { return res.status(404).json({ error: 'Agent not found' }); @@ -173,9 +276,6 @@ const getAgentHandler = async (req, res, expandProperties = false) => { agent.author = agent.author.toString(); - // @deprecated - isCollaborative replaced by ACL permissions - agent.isCollaborative = !!agent.isCollaborative; - // Check if agent is public const isPublic = await hasPublicPermission({ resourceType: ResourceType.AGENT, @@ -199,9 +299,6 @@ const getAgentHandler = async (req, res, expandProperties = false) => { author: agent.author, provider: agent.provider, model: agent.model, - projectIds: agent.projectIds, - // @deprecated - isCollaborative replaced by ACL permissions - isCollaborative: agent.isCollaborative, isPublic: agent.isPublic, version: agent.version, // Safe metadata @@ -243,10 +340,21 @@ const updateAgentHandler = async (req, res) => { updateData.avatar = avatarField; } + if (updateData.edges?.length) { + const { id: userId, role: userRole } = req.user; + const unauthorized = await validateEdgeAgentAccess(updateData.edges, userId, userRole); + if (unauthorized.length > 0) { + return res.status(403).json({ + error: 'You do not have access to one or more agents referenced in edges', + agent_ids: unauthorized, + }); + } + } + // Convert OCR to context in incoming updateData convertOcrToContextInPlace(updateData); - const existingAgent = await getAgent({ id }); + const existingAgent = await db.getAgent({ id }); if (!existingAgent) { return res.status(404).json({ error: 'Agent not found' }); @@ -261,9 +369,29 @@ const updateAgentHandler = async (req, res) => { updateData.tools = ocrConversion.tools; } + if (updateData.tools) { + const existingToolSet = new Set(existingAgent.tools ?? []); + const newMCPTools = updateData.tools.filter( + (t) => !existingToolSet.has(t) && t?.includes(Constants.mcp_delimiter), + ); + + if (newMCPTools.length > 0) { + const availableTools = (await getCachedTools()) ?? {}; + const approvedNew = await filterAuthorizedTools({ + tools: newMCPTools, + userId: req.user.id, + availableTools, + }); + const rejectedSet = new Set(newMCPTools.filter((t) => !approvedNew.includes(t))); + if (rejectedSet.size > 0) { + updateData.tools = updateData.tools.filter((t) => !rejectedSet.has(t)); + } + } + } + let updatedAgent = Object.keys(updateData).length > 0 - ? await updateAgent({ id }, updateData, { + ? await db.updateAgent({ id }, updateData, { updatingUserId: req.user.id, }) : existingAgent; @@ -313,7 +441,7 @@ const duplicateAgentHandler = async (req, res) => { const sensitiveFields = ['api_key', 'oauth_client_id', 'oauth_client_secret']; try { - const agent = await getAgent({ id }); + const agent = await db.getAgent({ id }); if (!agent) { return res.status(404).json({ error: 'Agent not found', @@ -361,7 +489,7 @@ const duplicateAgentHandler = async (req, res) => { }); const newActionsList = []; - const originalActions = (await getActions({ agent_id: id }, true)) ?? []; + const originalActions = (await db.getActions({ agent_id: id }, true)) ?? []; const promises = []; /** @@ -371,7 +499,7 @@ const duplicateAgentHandler = async (req, res) => { */ const duplicateAction = async (action) => { const newActionId = nanoid(); - const [domain] = action.action_id.split(actionDelimiter); + const { domain } = action.metadata; const fullActionId = `${domain}${actionDelimiter}${newActionId}`; // Sanitize sensitive metadata before persisting @@ -380,8 +508,8 @@ const duplicateAgentHandler = async (req, res) => { delete filteredMetadata[field]; } - const newAction = await updateAction( - { action_id: newActionId }, + const newAction = await db.updateAction( + { action_id: newActionId, agent_id: newAgentId }, { metadata: filteredMetadata, agent_id: newAgentId, @@ -403,7 +531,18 @@ const duplicateAgentHandler = async (req, res) => { const agentActions = await Promise.all(promises); newAgentData.actions = agentActions; - const newAgent = await createAgent(newAgentData); + + if (newAgentData.tools?.length) { + const availableTools = (await getCachedTools()) ?? {}; + newAgentData.tools = await filterAuthorizedTools({ + tools: newAgentData.tools, + userId, + availableTools, + existingTools: newAgentData.tools, + }); + } + + const newAgent = await db.createAgent(newAgentData); try { await Promise.all([ @@ -456,11 +595,11 @@ const duplicateAgentHandler = async (req, res) => { const deleteAgentHandler = async (req, res) => { try { const id = req.params.id; - const agent = await getAgent({ id }); + const agent = await db.getAgent({ id }); if (!agent) { return res.status(404).json({ error: 'Agent not found' }); } - await deleteAgent({ id }); + await db.deleteAgent({ id }); return res.json({ message: 'Agent deleted' }); } catch (error) { logger.error('[/Agents/:id] Error deleting Agent', error); @@ -530,31 +669,34 @@ const getListAgentsHandler = async (req, res) => { */ const cache = getLogStores(CacheKeys.S3_EXPIRY_INTERVAL); const refreshKey = `${userId}:agents_avatar_refresh`; - const alreadyChecked = await cache.get(refreshKey); - if (alreadyChecked) { - logger.debug('[/Agents] S3 avatar refresh already checked, skipping'); - } else { + let cachedRefresh = await cache.get(refreshKey); + const isValidCachedRefresh = + cachedRefresh != null && typeof cachedRefresh === 'object' && cachedRefresh.urlCache != null; + if (!isValidCachedRefresh) { try { - const fullList = await getListAgentsByAccess({ + const fullList = await db.getListAgentsByAccess({ accessibleIds, otherParams: {}, limit: MAX_AVATAR_REFRESH_AGENTS, after: null, }); - await refreshListAvatars({ + const { urlCache } = await refreshListAvatars({ agents: fullList?.data ?? [], userId, refreshS3Url, - updateAgent, + updateAgent: db.updateAgent, }); - await cache.set(refreshKey, true, Time.THIRTY_MINUTES); + cachedRefresh = { urlCache }; + await cache.set(refreshKey, cachedRefresh, Time.THIRTY_MINUTES); } catch (err) { logger.error('[/Agents] Error refreshing avatars for full list: %o', err); } + } else { + logger.debug('[/Agents] S3 avatar refresh already checked, skipping'); } // Use the new ACL-aware function - const data = await getListAgentsByAccess({ + const data = await db.getListAgentsByAccess({ accessibleIds, otherParams: filter, limit, @@ -568,11 +710,20 @@ const getListAgentsHandler = async (req, res) => { const publicSet = new Set(publiclyAccessibleIds.map((oid) => oid.toString())); + const urlCache = cachedRefresh?.urlCache; data.data = agents.map((agent) => { try { if (agent?._id && publicSet.has(agent._id.toString())) { agent.isPublic = true; } + if ( + urlCache && + agent?.id && + agent?.avatar?.source === FileSources.s3 && + urlCache[agent.id] + ) { + agent.avatar = { ...agent.avatar, filepath: urlCache[agent.id] }; + } } catch (e) { // Silently ignore mapping errors void e; @@ -610,7 +761,7 @@ const uploadAgentAvatarHandler = async (req, res) => { return res.status(400).json({ message: 'Agent ID is required' }); } - const existingAgent = await getAgent({ id: agent_id }); + const existingAgent = await db.getAgent({ id: agent_id }); if (!existingAgent) { return res.status(404).json({ error: 'Agent not found' }); @@ -642,7 +793,7 @@ const uploadAgentAvatarHandler = async (req, res) => { const { deleteFile } = getStrategyFunctions(_avatar.source); try { await deleteFile(req, { filepath: _avatar.filepath }); - await deleteFileByFilter({ user: req.user.id, filepath: _avatar.filepath }); + await db.deleteFileByFilter({ user: req.user.id, filepath: _avatar.filepath }); } catch (error) { logger.error('[/:agent_id/avatar] Error deleting old avatar', error); } @@ -655,9 +806,17 @@ const uploadAgentAvatarHandler = async (req, res) => { }, }; - const updatedAgent = await updateAgent({ id: agent_id }, data, { + const updatedAgent = await db.updateAgent({ id: agent_id }, data, { updatingUserId: req.user.id, }); + + try { + const avatarCache = getLogStores(CacheKeys.S3_EXPIRY_INTERVAL); + await avatarCache.delete(`${req.user.id}:agents_avatar_refresh`); + } catch (cacheErr) { + logger.error('[/:agent_id/avatar] Error invalidating avatar refresh cache', cacheErr); + } + res.status(201).json(updatedAgent); } catch (error) { const message = 'An error occurred while updating the Agent Avatar'; @@ -703,7 +862,7 @@ const revertAgentVersionHandler = async (req, res) => { return res.status(400).json({ error: 'version_index is required' }); } - const existingAgent = await getAgent({ id }); + const existingAgent = await db.getAgent({ id }); if (!existingAgent) { return res.status(404).json({ error: 'Agent not found' }); @@ -711,7 +870,24 @@ const revertAgentVersionHandler = async (req, res) => { // Permissions are enforced via route middleware (ACL EDIT) - const updatedAgent = await revertAgentVersion({ id }, version_index); + let updatedAgent = await db.revertAgentVersion({ id }, version_index); + + if (updatedAgent.tools?.length) { + const availableTools = (await getCachedTools()) ?? {}; + const filteredTools = await filterAuthorizedTools({ + tools: updatedAgent.tools, + userId: req.user.id, + availableTools, + existingTools: updatedAgent.tools, + }); + if (filteredTools.length !== updatedAgent.tools.length) { + updatedAgent = await db.updateAgent( + { id }, + { tools: filteredTools }, + { updatingUserId: req.user.id }, + ); + } + } if (updatedAgent.author) { updatedAgent.author = updatedAgent.author.toString(); @@ -735,8 +911,8 @@ const revertAgentVersionHandler = async (req, res) => { */ const getAgentCategories = async (_req, res) => { try { - const categories = await getCategoriesWithCounts(); - const promotedCount = await countPromotedAgents(); + const categories = await db.getCategoriesWithCounts(); + const promotedCount = await db.countPromotedAgents(); const formattedCategories = categories.map((category) => ({ value: category.value, label: category.label, @@ -779,4 +955,5 @@ module.exports = { uploadAgentAvatar: uploadAgentAvatarHandler, revertAgentVersion: revertAgentVersionHandler, getAgentCategories, + filterAuthorizedTools, }; diff --git a/api/server/controllers/agents/v1.spec.js b/api/server/controllers/agents/v1.spec.js index 8b2a57d903..455cea2e7c 100644 --- a/api/server/controllers/agents/v1.spec.js +++ b/api/server/controllers/agents/v1.spec.js @@ -2,7 +2,7 @@ const mongoose = require('mongoose'); const { nanoid } = require('nanoid'); const { v4: uuidv4 } = require('uuid'); const { agentSchema } = require('@librechat/data-schemas'); -const { FileSources } = require('librechat-data-provider'); +const { FileSources, PermissionBits } = require('librechat-data-provider'); const { MongoMemoryServer } = require('mongodb-memory-server'); // Only mock the dependencies that are not database-related @@ -14,10 +14,6 @@ jest.mock('~/server/services/Config', () => ({ }), })); -jest.mock('~/models/Project', () => ({ - getProjectByName: jest.fn().mockResolvedValue(null), -})); - jest.mock('~/server/services/Files/strategies', () => ({ getStrategyFunctions: jest.fn(), })); @@ -26,7 +22,16 @@ jest.mock('~/server/services/Files/images/avatar', () => ({ resizeAvatar: jest.fn(), })); -jest.mock('~/server/services/Files/S3/crud', () => ({ +jest.mock('sharp', () => + jest.fn(() => ({ + metadata: jest.fn().mockResolvedValue({}), + toFormat: jest.fn().mockReturnThis(), + toBuffer: jest.fn().mockResolvedValue(Buffer.alloc(0)), + })), +); + +jest.mock('@librechat/api', () => ({ + ...jest.requireActual('@librechat/api'), refreshS3Url: jest.fn(), })); @@ -34,31 +39,32 @@ jest.mock('~/server/services/Files/process', () => ({ filterFile: jest.fn(), })); -jest.mock('~/models/Action', () => ({ - updateAction: jest.fn(), - getActions: jest.fn().mockResolvedValue([]), -})); - -jest.mock('~/models/File', () => ({ - deleteFileByFilter: jest.fn(), -})); - jest.mock('~/server/services/PermissionService', () => ({ findAccessibleResources: jest.fn().mockResolvedValue([]), findPubliclyAccessibleResources: jest.fn().mockResolvedValue([]), + getResourcePermissionsMap: jest.fn().mockResolvedValue(new Map()), grantPermission: jest.fn(), hasPublicPermission: jest.fn().mockResolvedValue(false), - checkPermission: jest.fn().mockResolvedValue(true), })); -jest.mock('~/models', () => ({ - getCategoriesWithCounts: jest.fn(), -})); +jest.mock('~/models', () => { + const mongoose = require('mongoose'); + const { createMethods } = require('@librechat/data-schemas'); + const methods = createMethods(mongoose, { + removeAllPermissions: jest.fn().mockResolvedValue(undefined), + }); + return { + ...methods, + getCategoriesWithCounts: jest.fn(), + deleteFileByFilter: jest.fn(), + }; +}); // Mock cache for S3 avatar refresh tests const mockCache = { get: jest.fn(), set: jest.fn(), + delete: jest.fn(), }; jest.mock('~/cache', () => ({ getLogStores: jest.fn(() => mockCache), @@ -73,9 +79,10 @@ const { const { findAccessibleResources, findPubliclyAccessibleResources, + getResourcePermissionsMap, } = require('~/server/services/PermissionService'); -const { refreshS3Url } = require('~/server/services/Files/S3/crud'); +const { refreshS3Url } = require('@librechat/api'); /** * @type {import('mongoose').Model} @@ -174,7 +181,6 @@ describe('Agent Controllers - Mass Assignment Protection', () => { // Unauthorized fields that should be stripped author: new mongoose.Types.ObjectId().toString(), // Should not be able to set author authorName: 'Hacker', // Should be stripped - isCollaborative: true, // Should be stripped on creation versions: [], // Should be stripped _id: new mongoose.Types.ObjectId(), // Should be stripped id: 'custom_agent_id', // Should be overridden @@ -193,7 +199,6 @@ describe('Agent Controllers - Mass Assignment Protection', () => { // Verify unauthorized fields were not set expect(createdAgent.author.toString()).toBe(mockReq.user.id); // Should be the request user, not the malicious value expect(createdAgent.authorName).toBeUndefined(); - expect(createdAgent.isCollaborative).toBeFalsy(); expect(createdAgent.versions).toHaveLength(1); // Should have exactly 1 version from creation expect(createdAgent.id).not.toBe('custom_agent_id'); // Should have generated ID expect(createdAgent.id).toMatch(/^agent_/); // Should have proper prefix @@ -444,7 +449,6 @@ describe('Agent Controllers - Mass Assignment Protection', () => { model: 'gpt-3.5-turbo', author: existingAgentAuthorId, description: 'Original description', - isCollaborative: false, versions: [ { name: 'Original Agent', @@ -466,7 +470,6 @@ describe('Agent Controllers - Mass Assignment Protection', () => { name: 'Updated Agent', description: 'Updated description', model: 'gpt-4', - isCollaborative: true, // This IS allowed in updates }; await updateAgentHandler(mockReq, mockRes); @@ -479,13 +482,11 @@ describe('Agent Controllers - Mass Assignment Protection', () => { expect(updatedAgent.name).toBe('Updated Agent'); expect(updatedAgent.description).toBe('Updated description'); expect(updatedAgent.model).toBe('gpt-4'); - expect(updatedAgent.isCollaborative).toBe(true); expect(updatedAgent.author).toBe(existingAgentAuthorId.toString()); // Verify in database const agentInDb = await Agent.findOne({ id: existingAgentId }); expect(agentInDb.name).toBe('Updated Agent'); - expect(agentInDb.isCollaborative).toBe(true); }); test('should reject update with unauthorized fields (mass assignment protection)', async () => { @@ -540,26 +541,6 @@ describe('Agent Controllers - Mass Assignment Protection', () => { expect(updatedAgent.name).toBe('Admin Update'); }); - test('should handle projectIds updates', async () => { - mockReq.user.id = existingAgentAuthorId.toString(); - mockReq.params.id = existingAgentId; - - const projectId1 = new mongoose.Types.ObjectId().toString(); - const projectId2 = new mongoose.Types.ObjectId().toString(); - - mockReq.body = { - projectIds: [projectId1, projectId2], - }; - - await updateAgentHandler(mockReq, mockRes); - - expect(mockRes.json).toHaveBeenCalled(); - - const updatedAgent = mockRes.json.mock.calls[0][0]; - expect(updatedAgent).toBeDefined(); - // Note: updateAgentProjects requires more setup, so we just verify the handler doesn't crash - }); - test('should validate tool_resources in updates', async () => { mockReq.user.id = existingAgentAuthorId.toString(); mockReq.params.id = existingAgentId; @@ -1309,7 +1290,7 @@ describe('Agent Controllers - Mass Assignment Protection', () => { }); test('should skip avatar refresh if cache hit', async () => { - mockCache.get.mockResolvedValue(true); + mockCache.get.mockResolvedValue({ urlCache: {} }); findAccessibleResources.mockResolvedValue([agentWithS3Avatar._id]); findPubliclyAccessibleResources.mockResolvedValue([]); @@ -1348,8 +1329,12 @@ describe('Agent Controllers - Mass Assignment Protection', () => { // Verify S3 URL was refreshed expect(refreshS3Url).toHaveBeenCalled(); - // Verify cache was set - expect(mockCache.set).toHaveBeenCalled(); + // Verify cache was set with urlCache map, not a plain boolean + expect(mockCache.set).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ urlCache: expect.any(Object) }), + expect.any(Number), + ); // Verify response was returned expect(mockRes.json).toHaveBeenCalled(); @@ -1563,5 +1548,191 @@ describe('Agent Controllers - Mass Assignment Protection', () => { // Verify that the handler completed successfully expect(mockRes.json).toHaveBeenCalled(); }); + + test('should treat legacy boolean cache entry as a miss and run refresh', async () => { + // Simulate a cache entry written by the pre-fix code + mockCache.get.mockResolvedValue(true); + findAccessibleResources.mockResolvedValue([agentWithS3Avatar._id]); + findPubliclyAccessibleResources.mockResolvedValue([]); + refreshS3Url.mockResolvedValue('new-s3-path.jpg'); + + const mockReq = { + user: { id: userA.toString(), role: 'USER' }, + query: {}, + }; + const mockRes = { + status: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), + }; + + await getListAgentsHandler(mockReq, mockRes); + + // Boolean true fails the shape guard, so refresh must run + expect(refreshS3Url).toHaveBeenCalled(); + // Cache is overwritten with the proper format + expect(mockCache.set).toHaveBeenCalledWith( + expect.any(String), + expect.objectContaining({ urlCache: expect.any(Object) }), + expect.any(Number), + ); + }); + + test('should apply cached urlCache filepath to paginated response on cache hit', async () => { + const agentId = agentWithS3Avatar.id; + const cachedUrl = 'cached-presigned-url.jpg'; + + mockCache.get.mockResolvedValue({ urlCache: { [agentId]: cachedUrl } }); + findAccessibleResources.mockResolvedValue([agentWithS3Avatar._id]); + findPubliclyAccessibleResources.mockResolvedValue([]); + + const mockReq = { + user: { id: userA.toString(), role: 'USER' }, + query: {}, + }; + const mockRes = { + status: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), + }; + + await getListAgentsHandler(mockReq, mockRes); + + expect(refreshS3Url).not.toHaveBeenCalled(); + + const responseData = mockRes.json.mock.calls[0][0]; + const agent = responseData.data.find((a) => a.id === agentId); + // Cached URL is served, not the stale DB value 'old-s3-path.jpg' + expect(agent.avatar.filepath).toBe(cachedUrl); + }); + + test('should preserve DB filepath for agents absent from urlCache on cache hit', async () => { + mockCache.get.mockResolvedValue({ urlCache: {} }); + findAccessibleResources.mockResolvedValue([agentWithS3Avatar._id]); + findPubliclyAccessibleResources.mockResolvedValue([]); + + const mockReq = { + user: { id: userA.toString(), role: 'USER' }, + query: {}, + }; + const mockRes = { + status: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), + }; + + await getListAgentsHandler(mockReq, mockRes); + + expect(refreshS3Url).not.toHaveBeenCalled(); + + const responseData = mockRes.json.mock.calls[0][0]; + const agent = responseData.data.find((a) => a.id === agentWithS3Avatar.id); + expect(agent.avatar.filepath).toBe('old-s3-path.jpg'); + }); + }); + + describe('Edge ACL validation', () => { + let targetAgent; + + beforeEach(async () => { + targetAgent = await Agent.create({ + id: `agent_${nanoid()}`, + author: new mongoose.Types.ObjectId().toString(), + name: 'Target Agent', + provider: 'openai', + model: 'gpt-4', + tools: [], + }); + }); + + test('createAgentHandler should return 403 when user lacks VIEW on an edge-referenced agent', async () => { + const permMap = new Map(); + getResourcePermissionsMap.mockResolvedValueOnce(permMap); + + mockReq.body = { + name: 'Attacker Agent', + provider: 'openai', + model: 'gpt-4', + edges: [{ from: 'self_placeholder', to: targetAgent.id, edgeType: 'handoff' }], + }; + + await createAgentHandler(mockReq, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(403); + const response = mockRes.json.mock.calls[0][0]; + expect(response.agent_ids).toContain(targetAgent.id); + }); + + test('createAgentHandler should succeed when user has VIEW on all edge-referenced agents', async () => { + const permMap = new Map([[targetAgent._id.toString(), 1]]); + getResourcePermissionsMap.mockResolvedValueOnce(permMap); + + mockReq.body = { + name: 'Legit Agent', + provider: 'openai', + model: 'gpt-4', + edges: [{ from: 'self_placeholder', to: targetAgent.id, edgeType: 'handoff' }], + }; + + await createAgentHandler(mockReq, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(201); + }); + + test('createAgentHandler should allow edges referencing non-existent agents (self-reference at create time)', async () => { + mockReq.body = { + name: 'Self-Ref Agent', + provider: 'openai', + model: 'gpt-4', + edges: [{ from: 'agent_does_not_exist_yet', to: 'agent_also_new', edgeType: 'handoff' }], + }; + + await createAgentHandler(mockReq, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(201); + }); + + test('updateAgentHandler should return 403 when user lacks VIEW on an edge-referenced agent', async () => { + const ownedAgent = await Agent.create({ + id: `agent_${nanoid()}`, + author: mockReq.user.id, + name: 'Owned Agent', + provider: 'openai', + model: 'gpt-4', + tools: [], + }); + + const permMap = new Map([[ownedAgent._id.toString(), PermissionBits.VIEW]]); + getResourcePermissionsMap.mockResolvedValueOnce(permMap); + + mockReq.params = { id: ownedAgent.id }; + mockReq.body = { + edges: [{ from: ownedAgent.id, to: targetAgent.id, edgeType: 'handoff' }], + }; + + await updateAgentHandler(mockReq, mockRes); + + expect(mockRes.status).toHaveBeenCalledWith(403); + const response = mockRes.json.mock.calls[0][0]; + expect(response.agent_ids).toContain(targetAgent.id); + expect(response.agent_ids).not.toContain(ownedAgent.id); + }); + + test('updateAgentHandler should succeed when edges field is absent from payload', async () => { + const ownedAgent = await Agent.create({ + id: `agent_${nanoid()}`, + author: mockReq.user.id, + name: 'Owned Agent', + provider: 'openai', + model: 'gpt-4', + tools: [], + }); + + mockReq.params = { id: ownedAgent.id }; + mockReq.body = { name: 'Renamed Agent' }; + + await updateAgentHandler(mockReq, mockRes); + + expect(mockRes.status).not.toHaveBeenCalledWith(403); + const response = mockRes.json.mock.calls[0][0]; + expect(response.name).toBe('Renamed Agent'); + }); }); }); diff --git a/api/server/controllers/assistants/chatV1.js b/api/server/controllers/assistants/chatV1.js index f6244f0d48..e4a20c2a5e 100644 --- a/api/server/controllers/assistants/chatV1.js +++ b/api/server/controllers/assistants/chatV1.js @@ -1,7 +1,13 @@ const { v4 } = require('uuid'); const { sleep } = require('@librechat/agents'); const { logger } = require('@librechat/data-schemas'); -const { sendEvent, getBalanceConfig, countTokens } = require('@librechat/api'); +const { + sendEvent, + countTokens, + checkBalance, + getBalanceConfig, + getModelMaxTokens, +} = require('@librechat/api'); const { Time, Constants, @@ -12,7 +18,6 @@ const { EModelEndpoint, ViolationTypes, ImageVisionTool, - getModelMaxTokens, checkOpenAIStorage, AssistantStreamEvents, } = require('librechat-data-provider'); @@ -32,10 +37,14 @@ const { createRun, StreamRunManager } = require('~/server/services/Runs'); const { addTitle } = require('~/server/services/Endpoints/assistants'); const { createRunBody } = require('~/server/services/createRunBody'); const { sendResponse } = require('~/server/middleware/error'); -const { getTransactions } = require('~/models/Transaction'); -const { checkBalance } = require('~/models/balanceMethods'); -const { getConvo } = require('~/models/Conversation'); -const getLogStores = require('~/cache/getLogStores'); +const { + createAutoRefillTransaction, + findBalanceByUser, + getTransactions, + getMultiplier, + getConvo, +} = require('~/models'); +const { logViolation, getLogStores } = require('~/cache'); const { getOpenAIClient } = require('./helpers'); /** @@ -276,16 +285,19 @@ const chatV1 = async (req, res) => { // Count tokens up to the current context window promptTokens = Math.min(promptTokens, getModelMaxTokens(model)); - await checkBalance({ - req, - res, - txData: { - model, - user: req.user.id, - tokenType: 'prompt', - amount: promptTokens, + await checkBalance( + { + req, + res, + txData: { + model, + user: req.user.id, + tokenType: 'prompt', + amount: promptTokens, + }, }, - }); + { findBalanceByUser, getMultiplier, createAutoRefillTransaction, logViolation }, + ); }; const { openai: _openai } = await getOpenAIClient({ diff --git a/api/server/controllers/assistants/chatV2.js b/api/server/controllers/assistants/chatV2.js index 365a7ae3ce..559d9d8953 100644 --- a/api/server/controllers/assistants/chatV2.js +++ b/api/server/controllers/assistants/chatV2.js @@ -1,7 +1,13 @@ const { v4 } = require('uuid'); const { sleep } = require('@librechat/agents'); const { logger } = require('@librechat/data-schemas'); -const { sendEvent, getBalanceConfig, countTokens } = require('@librechat/api'); +const { + sendEvent, + countTokens, + checkBalance, + getBalanceConfig, + getModelMaxTokens, +} = require('@librechat/api'); const { Time, Constants, @@ -10,7 +16,6 @@ const { ContentTypes, ToolCallTypes, EModelEndpoint, - getModelMaxTokens, retrievalMimeTypes, AssistantStreamEvents, } = require('librechat-data-provider'); @@ -27,10 +32,14 @@ const validateAuthor = require('~/server/middleware/assistants/validateAuthor'); const { createRun, StreamRunManager } = require('~/server/services/Runs'); const { addTitle } = require('~/server/services/Endpoints/assistants'); const { createRunBody } = require('~/server/services/createRunBody'); -const { getTransactions } = require('~/models/Transaction'); -const { checkBalance } = require('~/models/balanceMethods'); -const { getConvo } = require('~/models/Conversation'); -const getLogStores = require('~/cache/getLogStores'); +const { + getConvo, + getMultiplier, + getTransactions, + findBalanceByUser, + createAutoRefillTransaction, +} = require('~/models'); +const { logViolation, getLogStores } = require('~/cache'); const { getOpenAIClient } = require('./helpers'); /** @@ -149,16 +158,19 @@ const chatV2 = async (req, res) => { // Count tokens up to the current context window promptTokens = Math.min(promptTokens, getModelMaxTokens(model)); - await checkBalance({ - req, - res, - txData: { - model, - user: req.user.id, - tokenType: 'prompt', - amount: promptTokens, + await checkBalance( + { + req, + res, + txData: { + model, + user: req.user.id, + tokenType: 'prompt', + amount: promptTokens, + }, }, - }); + { findBalanceByUser, getMultiplier, createAutoRefillTransaction, logViolation }, + ); }; const { openai: _openai } = await getOpenAIClient({ diff --git a/api/server/controllers/assistants/errors.js b/api/server/controllers/assistants/errors.js index 1ae12ea3d5..f8dcf39f2b 100644 --- a/api/server/controllers/assistants/errors.js +++ b/api/server/controllers/assistants/errors.js @@ -3,8 +3,8 @@ const { logger } = require('@librechat/data-schemas'); const { CacheKeys, ViolationTypes, ContentTypes } = require('librechat-data-provider'); const { recordUsage, checkMessageGaps } = require('~/server/services/Threads'); const { sendResponse } = require('~/server/middleware/error'); -const { getConvo } = require('~/models/Conversation'); const getLogStores = require('~/cache/getLogStores'); +const { getConvo } = require('~/models'); /** * @typedef {Object} ErrorHandlerContext diff --git a/api/server/controllers/assistants/helpers.js b/api/server/controllers/assistants/helpers.js index 9183680f1e..6309268770 100644 --- a/api/server/controllers/assistants/helpers.js +++ b/api/server/controllers/assistants/helpers.js @@ -1,14 +1,15 @@ const { - SystemRoles, EModelEndpoint, defaultOrderQuery, defaultAssistantsVersion, } = require('librechat-data-provider'); +const { logger, SystemCapabilities } = require('@librechat/data-schemas'); const { initializeClient: initAzureClient, } = require('~/server/services/Endpoints/azureAssistants'); const { initializeClient } = require('~/server/services/Endpoints/assistants'); const { getEndpointsConfig } = require('~/server/services/Config'); +const { hasCapability } = require('~/server/middleware'); /** * @param {ServerRequest} req @@ -236,9 +237,19 @@ const fetchAssistants = async ({ req, res, overrideEndpoint }) => { body = await listAssistantsForAzure({ req, res, version, azureConfig, query }); } - if (req.user.role === SystemRoles.ADMIN) { + if (!appConfig.endpoints?.[endpoint]) { return body; - } else if (!appConfig.endpoints?.[endpoint]) { + } + + let canManageAssistants = false; + try { + canManageAssistants = await hasCapability(req.user, SystemCapabilities.MANAGE_ASSISTANTS); + } catch (err) { + logger.warn(`[fetchAssistants] capability check failed, denying bypass: ${err.message}`); + } + + if (canManageAssistants) { + logger.debug(`[fetchAssistants] MANAGE_ASSISTANTS bypass for user ${req.user.id}`); return body; } diff --git a/api/server/controllers/assistants/v1.js b/api/server/controllers/assistants/v1.js index 5d13d30334..c441b7ec59 100644 --- a/api/server/controllers/assistants/v1.js +++ b/api/server/controllers/assistants/v1.js @@ -1,15 +1,14 @@ const fs = require('fs').promises; const { logger } = require('@librechat/data-schemas'); const { FileContext } = require('librechat-data-provider'); +const { deleteFileByFilter, updateAssistantDoc, getAssistants } = require('~/models'); const { uploadImageBuffer, filterFile } = require('~/server/services/Files/process'); const validateAuthor = require('~/server/middleware/assistants/validateAuthor'); const { getStrategyFunctions } = require('~/server/services/Files/strategies'); const { deleteAssistantActions } = require('~/server/services/ActionService'); -const { updateAssistantDoc, getAssistants } = require('~/models/Assistant'); const { getOpenAIClient, fetchAssistants } = require('./helpers'); const { getCachedTools } = require('~/server/services/Config'); const { manifestToolMap } = require('~/app/clients/tools'); -const { deleteFileByFilter } = require('~/models'); /** * Create an assistant. diff --git a/api/server/controllers/assistants/v2.js b/api/server/controllers/assistants/v2.js index b9c5cd709f..cc0e03916d 100644 --- a/api/server/controllers/assistants/v2.js +++ b/api/server/controllers/assistants/v2.js @@ -3,8 +3,8 @@ const { ToolCallTypes } = require('librechat-data-provider'); const validateAuthor = require('~/server/middleware/assistants/validateAuthor'); const { validateAndUpdateTool } = require('~/server/services/ActionService'); const { getCachedTools } = require('~/server/services/Config'); -const { updateAssistantDoc } = require('~/models/Assistant'); const { manifestToolMap } = require('~/app/clients/tools'); +const { updateAssistantDoc } = require('~/models'); const { getOpenAIClient } = require('./helpers'); /** diff --git a/api/server/controllers/auth/LogoutController.js b/api/server/controllers/auth/LogoutController.js index 0b3cf262b8..039ed630c2 100644 --- a/api/server/controllers/auth/LogoutController.js +++ b/api/server/controllers/auth/LogoutController.js @@ -8,13 +8,16 @@ const logoutController = async (req, res) => { const parsedCookies = req.headers.cookie ? cookies.parse(req.headers.cookie) : {}; const isOpenIdUser = req.user?.openidId != null && req.user?.provider === 'openid'; - /** For OpenID users, read refresh token from session; for others, use cookie */ + /** For OpenID users, read tokens from session (with cookie fallback) */ let refreshToken; + let idToken; if (isOpenIdUser && req.session?.openidTokens) { refreshToken = req.session.openidTokens.refreshToken; + idToken = req.session.openidTokens.idToken; delete req.session.openidTokens; } refreshToken = refreshToken || parsedCookies.refreshToken; + idToken = idToken || parsedCookies.openid_id_token; try { const logout = await logoutUser(req, refreshToken); @@ -31,21 +34,34 @@ const logoutController = async (req, res) => { isEnabled(process.env.OPENID_USE_END_SESSION_ENDPOINT) && process.env.OPENID_ISSUER ) { - const openIdConfig = getOpenIdConfig(); - if (!openIdConfig) { - logger.warn( - '[logoutController] OpenID config not found. Please verify that the open id configuration and initialization are correct.', - ); - } else { - const endSessionEndpoint = openIdConfig - ? openIdConfig.serverMetadata().end_session_endpoint - : null; + let openIdConfig; + try { + openIdConfig = getOpenIdConfig(); + } catch (err) { + logger.warn('[logoutController] OpenID config not available:', err.message); + } + if (openIdConfig) { + const endSessionEndpoint = openIdConfig.serverMetadata().end_session_endpoint; if (endSessionEndpoint) { const endSessionUrl = new URL(endSessionEndpoint); /** Redirect back to app's login page after IdP logout */ const postLogoutRedirectUri = process.env.OPENID_POST_LOGOUT_REDIRECT_URI || `${process.env.DOMAIN_CLIENT}/login`; endSessionUrl.searchParams.set('post_logout_redirect_uri', postLogoutRedirectUri); + + /** Add id_token_hint (preferred) or client_id for OIDC spec compliance */ + if (idToken) { + endSessionUrl.searchParams.set('id_token_hint', idToken); + } else if (process.env.OPENID_CLIENT_ID) { + endSessionUrl.searchParams.set('client_id', process.env.OPENID_CLIENT_ID); + } else { + logger.warn( + '[logoutController] Neither id_token_hint nor OPENID_CLIENT_ID is available. ' + + 'To enable id_token_hint, set OPENID_REUSE_TOKENS=true. ' + + 'The OIDC end-session request may be rejected by the identity provider.', + ); + } + response.redirect = endSessionUrl.toString(); } else { logger.warn( diff --git a/api/server/controllers/auth/LogoutController.spec.js b/api/server/controllers/auth/LogoutController.spec.js new file mode 100644 index 0000000000..3f2a2de8e1 --- /dev/null +++ b/api/server/controllers/auth/LogoutController.spec.js @@ -0,0 +1,259 @@ +const cookies = require('cookie'); + +const mockLogoutUser = jest.fn(); +const mockLogger = { warn: jest.fn(), error: jest.fn() }; +const mockIsEnabled = jest.fn(); +const mockGetOpenIdConfig = jest.fn(); + +jest.mock('cookie'); +jest.mock('@librechat/api', () => ({ isEnabled: (...args) => mockIsEnabled(...args) })); +jest.mock('@librechat/data-schemas', () => ({ logger: mockLogger })); +jest.mock('~/server/services/AuthService', () => ({ + logoutUser: (...args) => mockLogoutUser(...args), +})); +jest.mock('~/strategies', () => ({ getOpenIdConfig: () => mockGetOpenIdConfig() })); + +const { logoutController } = require('./LogoutController'); + +function buildReq(overrides = {}) { + return { + user: { _id: 'user1', openidId: 'oid1', provider: 'openid' }, + headers: { cookie: 'refreshToken=rt1' }, + session: { + openidTokens: { refreshToken: 'srt', idToken: 'small-id-token' }, + destroy: jest.fn(), + }, + ...overrides, + }; +} + +function buildRes() { + const res = { + status: jest.fn().mockReturnThis(), + send: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), + clearCookie: jest.fn(), + }; + return res; +} + +const ORIGINAL_ENV = process.env; + +beforeEach(() => { + jest.clearAllMocks(); + process.env = { + ...ORIGINAL_ENV, + OPENID_USE_END_SESSION_ENDPOINT: 'true', + OPENID_ISSUER: 'https://idp.example.com', + OPENID_CLIENT_ID: 'my-client-id', + DOMAIN_CLIENT: 'https://app.example.com', + }; + cookies.parse.mockReturnValue({ refreshToken: 'cookie-rt' }); + mockLogoutUser.mockResolvedValue({ status: 200, message: 'Logout successful' }); + mockIsEnabled.mockReturnValue(true); + mockGetOpenIdConfig.mockReturnValue({ + serverMetadata: () => ({ + end_session_endpoint: 'https://idp.example.com/logout', + }), + }); +}); + +afterAll(() => { + process.env = ORIGINAL_ENV; +}); + +describe('LogoutController', () => { + describe('id_token_hint from session', () => { + it('sets id_token_hint when session has idToken', async () => { + const req = buildReq(); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + expect(body.redirect).toContain('id_token_hint=small-id-token'); + expect(body.redirect).not.toContain('client_id='); + }); + }); + + describe('id_token_hint from cookie fallback', () => { + it('uses cookie id_token when session has no tokens', async () => { + cookies.parse.mockReturnValue({ + refreshToken: 'cookie-rt', + openid_id_token: 'cookie-id-token', + }); + const req = buildReq({ session: { destroy: jest.fn() } }); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + expect(body.redirect).toContain('id_token_hint=cookie-id-token'); + }); + }); + + describe('client_id fallback', () => { + it('falls back to client_id when no idToken is available', async () => { + cookies.parse.mockReturnValue({ refreshToken: 'cookie-rt' }); + const req = buildReq({ session: { destroy: jest.fn() } }); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + expect(body.redirect).toContain('client_id=my-client-id'); + expect(body.redirect).not.toContain('id_token_hint='); + }); + + it('does not produce client_id=undefined when OPENID_CLIENT_ID is unset', async () => { + delete process.env.OPENID_CLIENT_ID; + cookies.parse.mockReturnValue({ refreshToken: 'cookie-rt' }); + const req = buildReq({ session: { destroy: jest.fn() } }); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + expect(body.redirect).not.toContain('client_id='); + expect(body.redirect).not.toContain('undefined'); + expect(mockLogger.warn).toHaveBeenCalledWith( + expect.stringContaining('Neither id_token_hint nor OPENID_CLIENT_ID'), + ); + }); + }); + + describe('OPENID_USE_END_SESSION_ENDPOINT disabled', () => { + it('does not include redirect when disabled', async () => { + mockIsEnabled.mockReturnValue(false); + const req = buildReq(); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + expect(body.redirect).toBeUndefined(); + }); + }); + + describe('OPENID_ISSUER unset', () => { + it('does not include redirect when OPENID_ISSUER is missing', async () => { + delete process.env.OPENID_ISSUER; + const req = buildReq(); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + expect(body.redirect).toBeUndefined(); + }); + }); + + describe('non-OpenID user', () => { + it('does not include redirect for non-OpenID users', async () => { + const req = buildReq({ + user: { _id: 'user1', provider: 'local' }, + }); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + expect(body.redirect).toBeUndefined(); + }); + }); + + describe('post_logout_redirect_uri', () => { + it('uses OPENID_POST_LOGOUT_REDIRECT_URI when set', async () => { + process.env.OPENID_POST_LOGOUT_REDIRECT_URI = 'https://custom.example.com/logged-out'; + const req = buildReq(); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + const url = new URL(body.redirect); + expect(url.searchParams.get('post_logout_redirect_uri')).toBe( + 'https://custom.example.com/logged-out', + ); + }); + + it('defaults to DOMAIN_CLIENT/login when OPENID_POST_LOGOUT_REDIRECT_URI is unset', async () => { + delete process.env.OPENID_POST_LOGOUT_REDIRECT_URI; + const req = buildReq(); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + const url = new URL(body.redirect); + expect(url.searchParams.get('post_logout_redirect_uri')).toBe( + 'https://app.example.com/login', + ); + }); + }); + + describe('OpenID config not available', () => { + it('warns and returns no redirect when getOpenIdConfig throws', async () => { + mockGetOpenIdConfig.mockImplementation(() => { + throw new Error('OpenID configuration has not been initialized'); + }); + const req = buildReq(); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + expect(body.redirect).toBeUndefined(); + expect(mockLogger.warn).toHaveBeenCalledWith( + expect.stringContaining('OpenID config not available'), + 'OpenID configuration has not been initialized', + ); + }); + }); + + describe('end_session_endpoint not in metadata', () => { + it('warns and returns no redirect when end_session_endpoint is missing', async () => { + mockGetOpenIdConfig.mockReturnValue({ + serverMetadata: () => ({}), + }); + const req = buildReq(); + const res = buildRes(); + + await logoutController(req, res); + + const body = res.send.mock.calls[0][0]; + expect(body.redirect).toBeUndefined(); + expect(mockLogger.warn).toHaveBeenCalledWith( + expect.stringContaining('end_session_endpoint not found'), + ); + }); + }); + + describe('error handling', () => { + it('returns 500 on logoutUser error', async () => { + mockLogoutUser.mockRejectedValue(new Error('session error')); + const req = buildReq(); + const res = buildRes(); + + await logoutController(req, res); + + expect(res.status).toHaveBeenCalledWith(500); + expect(res.json).toHaveBeenCalledWith({ message: 'session error' }); + }); + }); + + describe('cookie clearing', () => { + it('clears all auth cookies on successful logout', async () => { + const req = buildReq(); + const res = buildRes(); + + await logoutController(req, res); + + expect(res.clearCookie).toHaveBeenCalledWith('refreshToken'); + expect(res.clearCookie).toHaveBeenCalledWith('openid_access_token'); + expect(res.clearCookie).toHaveBeenCalledWith('openid_id_token'); + expect(res.clearCookie).toHaveBeenCalledWith('openid_user_id'); + expect(res.clearCookie).toHaveBeenCalledWith('token_provider'); + }); + }); +}); diff --git a/api/server/controllers/mcp.js b/api/server/controllers/mcp.js index e5dfff61ca..729f01da9d 100644 --- a/api/server/controllers/mcp.js +++ b/api/server/controllers/mcp.js @@ -7,9 +7,11 @@ */ const { logger } = require('@librechat/data-schemas'); const { + MCPErrorCodes, + redactServerSecrets, + redactAllServerSecrets, isMCPDomainNotAllowedError, isMCPInspectionFailedError, - MCPErrorCodes, } = require('@librechat/api'); const { Constants, MCPServerUserInputSchema } = require('librechat-data-provider'); const { cacheMCPServerTools, getMCPServerTools } = require('~/server/services/Config'); @@ -181,10 +183,8 @@ const getMCPServersList = async (req, res) => { return res.status(401).json({ message: 'Unauthorized' }); } - // 2. Get all server configs from registry (YAML + DB) const serverConfigs = await getMCPServersRegistry().getAllServerConfigs(userId); - - return res.json(serverConfigs); + return res.json(redactAllServerSecrets(serverConfigs)); } catch (error) { logger.error('[getMCPServersList]', error); res.status(500).json({ error: error.message }); @@ -215,7 +215,7 @@ const createMCPServerController = async (req, res) => { ); res.status(201).json({ serverName: result.serverName, - ...result.config, + ...redactServerSecrets(result.config), }); } catch (error) { logger.error('[createMCPServer]', error); @@ -243,7 +243,7 @@ const getMCPServerById = async (req, res) => { return res.status(404).json({ message: 'MCP server not found' }); } - res.status(200).json(parsedConfig); + res.status(200).json(redactServerSecrets(parsedConfig)); } catch (error) { logger.error('[getMCPServerById]', error); res.status(500).json({ message: error.message }); @@ -274,7 +274,7 @@ const updateMCPServerController = async (req, res) => { userId, ); - res.status(200).json(parsedConfig); + res.status(200).json(redactServerSecrets(parsedConfig)); } catch (error) { logger.error('[updateMCPServer]', error); const mcpErrorResponse = handleMCPError(error, res); diff --git a/api/server/controllers/tools.js b/api/server/controllers/tools.js index 14a757e2bc..1df11b1059 100644 --- a/api/server/controllers/tools.js +++ b/api/server/controllers/tools.js @@ -9,13 +9,11 @@ const { ToolCallTypes, PermissionTypes, } = require('librechat-data-provider'); +const { getRoleByName, createToolCall, getToolCallsByConvo, getMessage } = require('~/models'); const { processFileURL, uploadImageBuffer } = require('~/server/services/Files/process'); const { processCodeOutput } = require('~/server/services/Files/Code/process'); -const { createToolCall, getToolCallsByConvo } = require('~/models/ToolCall'); const { loadAuthValues } = require('~/server/services/Tools/credentials'); const { loadTools } = require('~/app/clients/tools/util'); -const { getRoleByName } = require('~/models/Role'); -const { getMessage } = require('~/models/Message'); const fieldsMap = { [Tools.execute_code]: [EnvVar.CODE_API_KEY], diff --git a/api/server/experimental.js b/api/server/experimental.js index 4a457abf61..8982b69afb 100644 --- a/api/server/experimental.js +++ b/api/server/experimental.js @@ -14,6 +14,7 @@ const { logger } = require('@librechat/data-schemas'); const mongoSanitize = require('express-mongo-sanitize'); const { isEnabled, + apiNotFound, ErrorController, performStartupChecks, handleJsonParseError, @@ -23,14 +24,14 @@ const { connectDb, indexSync } = require('~/db'); const initializeOAuthReconnectManager = require('./services/initializeOAuthReconnectManager'); const createValidateImageRequest = require('./middleware/validateImageRequest'); const { jwtLogin, ldapLogin, passportLogin } = require('~/strategies'); -const { updateInterfacePermissions } = require('~/models/interface'); +const { updateInterfacePermissions: updateInterfacePerms } = require('@librechat/api'); +const { getRoleByName, updateAccessPermissions, seedDatabase } = require('~/models'); const { checkMigrations } = require('./services/start/migration'); const initializeMCPs = require('./services/initializeMCPs'); const configureSocialLogins = require('./socialLogins'); const { getAppConfig } = require('./services/Config'); const staticCache = require('./utils/staticCache'); const noIndex = require('./middleware/noIndex'); -const { seedDatabase } = require('~/models'); const routes = require('./routes'); const { PORT, HOST, ALLOW_SOCIAL_LOGIN, DISABLE_COMPRESSION, TRUST_PROXY } = process.env ?? {}; @@ -221,7 +222,7 @@ if (cluster.isMaster) { const appConfig = await getAppConfig(); initializeFileStorage(appConfig); await performStartupChecks(appConfig); - await updateInterfacePermissions(appConfig); + await updateInterfacePerms({ appConfig, getRoleByName, updateAccessPermissions }); /** Load index.html for SPA serving */ const indexPath = path.join(appConfig.paths.dist, 'index.html'); @@ -297,6 +298,7 @@ if (cluster.isMaster) { /** Routes */ app.use('/oauth', routes.oauth); app.use('/api/auth', routes.auth); + app.use('/api/admin', routes.adminAuth); app.use('/api/actions', routes.actions); app.use('/api/keys', routes.keys); app.use('/api/api-keys', routes.apiKeys); @@ -310,7 +312,6 @@ if (cluster.isMaster) { app.use('/api/endpoints', routes.endpoints); app.use('/api/balance', routes.balance); app.use('/api/models', routes.models); - app.use('/api/plugins', routes.plugins); app.use('/api/config', routes.config); app.use('/api/assistants', routes.assistants); app.use('/api/files', await routes.files.initialize()); @@ -324,8 +325,8 @@ if (cluster.isMaster) { app.use('/api/tags', routes.tags); app.use('/api/mcp', routes.mcp); - /** Error handler */ - app.use(ErrorController); + /** 404 for unmatched API routes */ + app.use('/api', apiNotFound); /** SPA fallback - serve index.html for all unmatched routes */ app.use((req, res) => { @@ -343,6 +344,9 @@ if (cluster.isMaster) { res.send(updatedIndexHtml); }); + /** Error handler (must be last - Express identifies error middleware by its 4-arg signature) */ + app.use(ErrorController); + /** Start listening on shared port (cluster will distribute connections) */ app.listen(port, host, async (err) => { if (err) { diff --git a/api/server/index.js b/api/server/index.js index fcd0229c9f..ba376ab335 100644 --- a/api/server/index.js +++ b/api/server/index.js @@ -12,25 +12,28 @@ const { logger } = require('@librechat/data-schemas'); const mongoSanitize = require('express-mongo-sanitize'); const { isEnabled, + apiNotFound, ErrorController, + memoryDiagnostics, performStartupChecks, handleJsonParseError, - initializeFileStorage, GenerationJobManager, createStreamServices, + initializeFileStorage, + updateInterfacePermissions, } = require('@librechat/api'); const { connectDb, indexSync } = require('~/db'); const initializeOAuthReconnectManager = require('./services/initializeOAuthReconnectManager'); +const { getRoleByName, updateAccessPermissions, seedDatabase } = require('~/models'); +const { capabilityContextMiddleware } = require('./middleware/roles/capabilities'); const createValidateImageRequest = require('./middleware/validateImageRequest'); const { jwtLogin, ldapLogin, passportLogin } = require('~/strategies'); -const { updateInterfacePermissions } = require('~/models/interface'); const { checkMigrations } = require('./services/start/migration'); const initializeMCPs = require('./services/initializeMCPs'); const configureSocialLogins = require('./socialLogins'); const { getAppConfig } = require('./services/Config'); const staticCache = require('./utils/staticCache'); const noIndex = require('./middleware/noIndex'); -const { seedDatabase } = require('~/models'); const routes = require('./routes'); const { PORT, HOST, ALLOW_SOCIAL_LOGIN, DISABLE_COMPRESSION, TRUST_PROXY } = process.env ?? {}; @@ -60,7 +63,7 @@ const startServer = async () => { const appConfig = await getAppConfig(); initializeFileStorage(appConfig); await performStartupChecks(appConfig); - await updateInterfacePermissions(appConfig); + await updateInterfacePermissions({ appConfig, getRoleByName, updateAccessPermissions }); const indexPath = path.join(appConfig.paths.dist, 'index.html'); let indexHTML = fs.readFileSync(indexPath, 'utf8'); @@ -131,6 +134,9 @@ const startServer = async () => { await configureSocialLogins(app); } + /* Per-request capability cache — must be registered before any route that calls hasCapability */ + app.use(capabilityContextMiddleware); + app.use('/oauth', routes.oauth); /* API Endpoints */ app.use('/api/auth', routes.auth); @@ -162,8 +168,10 @@ const startServer = async () => { app.use('/api/tags', routes.tags); app.use('/api/mcp', routes.mcp); - app.use(ErrorController); + /** 404 for unmatched API routes */ + app.use('/api', apiNotFound); + /** SPA fallback - serve index.html for all unmatched routes */ app.use((req, res) => { res.set({ 'Cache-Control': process.env.INDEX_CACHE_CONTROL || 'no-cache, no-store, must-revalidate', @@ -179,6 +187,9 @@ const startServer = async () => { res.send(updatedIndexHtml); }); + /** Error handler (must be last - Express identifies error middleware by its 4-arg signature) */ + app.use(ErrorController); + app.listen(port, host, async (err) => { if (err) { logger.error('Failed to start server:', err); @@ -201,6 +212,11 @@ const startServer = async () => { const streamServices = createStreamServices(); GenerationJobManager.configure(streamServices); GenerationJobManager.initialize(); + + const inspectFlags = process.execArgv.some((arg) => arg.startsWith('--inspect')); + if (inspectFlags || isEnabled(process.env.MEM_DIAG)) { + memoryDiagnostics.start(); + } }); }; @@ -251,6 +267,15 @@ process.on('uncaughtException', (err) => { return; } + if (isEnabled(process.env.CONTINUE_ON_UNCAUGHT_EXCEPTION)) { + logger.error('Unhandled error encountered. The app will continue running.', { + name: err?.name, + message: err?.message, + stack: err?.stack, + }); + return; + } + process.exit(1); }); diff --git a/api/server/index.spec.js b/api/server/index.spec.js index c73c605518..7b3d062fce 100644 --- a/api/server/index.spec.js +++ b/api/server/index.spec.js @@ -100,6 +100,40 @@ describe('Server Configuration', () => { expect(response.headers['expires']).toBe('0'); }); + it('should return 404 JSON for undefined API routes', async () => { + const response = await request(app).get('/api/nonexistent'); + expect(response.status).toBe(404); + expect(response.body).toEqual({ message: 'Endpoint not found' }); + }); + + it('should return 404 JSON for nested undefined API routes', async () => { + const response = await request(app).get('/api/nonexistent/nested/path'); + expect(response.status).toBe(404); + expect(response.body).toEqual({ message: 'Endpoint not found' }); + }); + + it('should return 404 JSON for non-GET methods on undefined API routes', async () => { + const post = await request(app).post('/api/nonexistent'); + expect(post.status).toBe(404); + expect(post.body).toEqual({ message: 'Endpoint not found' }); + + const del = await request(app).delete('/api/nonexistent'); + expect(del.status).toBe(404); + expect(del.body).toEqual({ message: 'Endpoint not found' }); + }); + + it('should return 404 JSON for the /api root path', async () => { + const response = await request(app).get('/api'); + expect(response.status).toBe(404); + expect(response.body).toEqual({ message: 'Endpoint not found' }); + }); + + it('should serve SPA HTML for non-API unmatched routes', async () => { + const response = await request(app).get('/this/does/not/exist'); + expect(response.status).toBe(200); + expect(response.headers['content-type']).toMatch(/html/); + }); + it('should return 500 for unknown errors via ErrorController', async () => { // Testing the error handling here on top of unit tests to ensure the middleware is correctly integrated diff --git a/api/server/middleware/abortMiddleware.js b/api/server/middleware/abortMiddleware.js index d07a09682d..e0c5ae0ff0 100644 --- a/api/server/middleware/abortMiddleware.js +++ b/api/server/middleware/abortMiddleware.js @@ -1,18 +1,18 @@ const { logger } = require('@librechat/data-schemas'); +const { isAssistantsEndpoint, ErrorTypes } = require('librechat-data-provider'); const { - countTokens, isEnabled, sendEvent, + countTokens, GenerationJobManager, + recordCollectedUsage, sanitizeMessageForTransmit, } = require('@librechat/api'); -const { isAssistantsEndpoint, ErrorTypes } = require('librechat-data-provider'); -const { spendTokens, spendStructuredTokens } = require('~/models/spendTokens'); const { truncateText, smartTruncateText } = require('~/app/clients/prompts'); const clearPendingReq = require('~/cache/clearPendingReq'); const { sendError } = require('~/server/middleware/error'); -const { saveMessage, getConvo } = require('~/models'); const { abortRun } = require('./abortRun'); +const db = require('~/models'); /** * Spend tokens for all models from collected usage. @@ -27,62 +27,35 @@ const { abortRun } = require('./abortRun'); * @param {string} params.conversationId - Conversation ID * @param {Array} params.collectedUsage - Usage metadata from all models * @param {string} [params.fallbackModel] - Fallback model name if not in usage + * @param {string} [params.messageId] - The response message ID for transaction correlation */ -async function spendCollectedUsage({ userId, conversationId, collectedUsage, fallbackModel }) { +async function spendCollectedUsage({ + userId, + conversationId, + collectedUsage, + fallbackModel, + messageId, +}) { if (!collectedUsage || collectedUsage.length === 0) { return; } - const spendPromises = []; - - for (const usage of collectedUsage) { - if (!usage) { - continue; - } - - // Support both OpenAI format (input_token_details) and Anthropic format (cache_*_input_tokens) - const cache_creation = - Number(usage.input_token_details?.cache_creation) || - Number(usage.cache_creation_input_tokens) || - 0; - const cache_read = - Number(usage.input_token_details?.cache_read) || Number(usage.cache_read_input_tokens) || 0; - - const txMetadata = { - context: 'abort', - conversationId, + await recordCollectedUsage( + { + spendTokens: db.spendTokens, + spendStructuredTokens: db.spendStructuredTokens, + pricing: { getMultiplier: db.getMultiplier, getCacheMultiplier: db.getCacheMultiplier }, + bulkWriteOps: { insertMany: db.bulkInsertTransactions, updateBalance: db.updateBalance }, + }, + { user: userId, - model: usage.model ?? fallbackModel, - }; - - if (cache_creation > 0 || cache_read > 0) { - spendPromises.push( - spendStructuredTokens(txMetadata, { - promptTokens: { - input: usage.input_tokens, - write: cache_creation, - read: cache_read, - }, - completionTokens: usage.output_tokens, - }).catch((err) => { - logger.error('[abortMiddleware] Error spending structured tokens for abort', err); - }), - ); - continue; - } - - spendPromises.push( - spendTokens(txMetadata, { - promptTokens: usage.input_tokens, - completionTokens: usage.output_tokens, - }).catch((err) => { - logger.error('[abortMiddleware] Error spending tokens for abort', err); - }), - ); - } - - // Wait for all token spending to complete - await Promise.all(spendPromises); + conversationId, + collectedUsage, + context: 'abort', + messageId, + model: fallbackModel, + }, + ); // Clear the array to prevent double-spending from the AgentClient finally block. // The collectedUsage array is shared by reference with AgentClient.collectedUsage, @@ -144,23 +117,28 @@ async function abortMessage(req, res) { conversationId: jobData?.conversationId, collectedUsage, fallbackModel: jobData?.model, + messageId: jobData?.responseMessageId, }); } else { // Fallback: no collected usage, use text-based token counting for primary model only - await spendTokens( + await db.spendTokens( { ...responseMessage, context: 'incomplete', user: userId }, { promptTokens, completionTokens }, ); } - await saveMessage( - req, + await db.saveMessage( + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { ...responseMessage, user: userId }, { context: 'api/server/middleware/abortMiddleware.js' }, ); // Get conversation for title - const conversation = await getConvo(userId, conversationId); + const conversation = await db.getConvo(userId, conversationId); const finalEvent = { title: conversation && !conversation.title ? null : conversation?.title || 'New Chat', @@ -292,4 +270,5 @@ const handleAbortError = async (res, req, error, data) => { module.exports = { handleAbort, handleAbortError, + spendCollectedUsage, }; diff --git a/api/server/middleware/abortMiddleware.spec.js b/api/server/middleware/abortMiddleware.spec.js index 93f2ce558b..a4ce85674b 100644 --- a/api/server/middleware/abortMiddleware.spec.js +++ b/api/server/middleware/abortMiddleware.spec.js @@ -4,15 +4,21 @@ * This tests the token spending logic for abort scenarios, * particularly for parallel agents (addedConvo) where multiple * models need their tokens spent. + * + * spendCollectedUsage delegates to recordCollectedUsage from @librechat/api, + * passing pricing + bulkWriteOps deps, with context: 'abort'. + * After spending, it clears the collectedUsage array to prevent double-spending + * from the AgentClient finally block (which shares the same array reference). */ const mockSpendTokens = jest.fn().mockResolvedValue(); const mockSpendStructuredTokens = jest.fn().mockResolvedValue(); +const mockRecordCollectedUsage = jest + .fn() + .mockResolvedValue({ input_tokens: 100, output_tokens: 50 }); -jest.mock('~/models/spendTokens', () => ({ - spendTokens: (...args) => mockSpendTokens(...args), - spendStructuredTokens: (...args) => mockSpendStructuredTokens(...args), -})); +const mockGetMultiplier = jest.fn().mockReturnValue(1); +const mockGetCacheMultiplier = jest.fn().mockReturnValue(null); jest.mock('@librechat/data-schemas', () => ({ logger: { @@ -30,6 +36,7 @@ jest.mock('@librechat/api', () => ({ GenerationJobManager: { abortJob: jest.fn(), }, + recordCollectedUsage: mockRecordCollectedUsage, sanitizeMessageForTransmit: jest.fn((msg) => msg), })); @@ -49,94 +56,31 @@ jest.mock('~/server/middleware/error', () => ({ sendError: jest.fn(), })); +const mockUpdateBalance = jest.fn().mockResolvedValue({}); +const mockBulkInsertTransactions = jest.fn().mockResolvedValue(undefined); jest.mock('~/models', () => ({ saveMessage: jest.fn().mockResolvedValue(), getConvo: jest.fn().mockResolvedValue({ title: 'Test Chat' }), + updateBalance: mockUpdateBalance, + bulkInsertTransactions: mockBulkInsertTransactions, + spendTokens: (...args) => mockSpendTokens(...args), + spendStructuredTokens: (...args) => mockSpendStructuredTokens(...args), + getMultiplier: mockGetMultiplier, + getCacheMultiplier: mockGetCacheMultiplier, })); jest.mock('./abortRun', () => ({ abortRun: jest.fn(), })); -// Import the module after mocks are set up -// We need to extract the spendCollectedUsage function for testing -// Since it's not exported, we'll test it through the handleAbort flow +const { spendCollectedUsage } = require('./abortMiddleware'); describe('abortMiddleware - spendCollectedUsage', () => { beforeEach(() => { jest.clearAllMocks(); }); - describe('spendCollectedUsage logic', () => { - // Since spendCollectedUsage is not exported, we test the logic directly - // by replicating the function here for unit testing - - const spendCollectedUsage = async ({ - userId, - conversationId, - collectedUsage, - fallbackModel, - }) => { - if (!collectedUsage || collectedUsage.length === 0) { - return; - } - - const spendPromises = []; - - for (const usage of collectedUsage) { - if (!usage) { - continue; - } - - const cache_creation = - Number(usage.input_token_details?.cache_creation) || - Number(usage.cache_creation_input_tokens) || - 0; - const cache_read = - Number(usage.input_token_details?.cache_read) || - Number(usage.cache_read_input_tokens) || - 0; - - const txMetadata = { - context: 'abort', - conversationId, - user: userId, - model: usage.model ?? fallbackModel, - }; - - if (cache_creation > 0 || cache_read > 0) { - spendPromises.push( - mockSpendStructuredTokens(txMetadata, { - promptTokens: { - input: usage.input_tokens, - write: cache_creation, - read: cache_read, - }, - completionTokens: usage.output_tokens, - }).catch(() => { - // Log error but don't throw - }), - ); - continue; - } - - spendPromises.push( - mockSpendTokens(txMetadata, { - promptTokens: usage.input_tokens, - completionTokens: usage.output_tokens, - }).catch(() => { - // Log error but don't throw - }), - ); - } - - // Wait for all token spending to complete - await Promise.all(spendPromises); - - // Clear the array to prevent double-spending - collectedUsage.length = 0; - }; - + describe('spendCollectedUsage delegation', () => { it('should return early if collectedUsage is empty', async () => { await spendCollectedUsage({ userId: 'user-123', @@ -145,8 +89,7 @@ describe('abortMiddleware - spendCollectedUsage', () => { fallbackModel: 'gpt-4', }); - expect(mockSpendTokens).not.toHaveBeenCalled(); - expect(mockSpendStructuredTokens).not.toHaveBeenCalled(); + expect(mockRecordCollectedUsage).not.toHaveBeenCalled(); }); it('should return early if collectedUsage is null', async () => { @@ -157,28 +100,10 @@ describe('abortMiddleware - spendCollectedUsage', () => { fallbackModel: 'gpt-4', }); - expect(mockSpendTokens).not.toHaveBeenCalled(); - expect(mockSpendStructuredTokens).not.toHaveBeenCalled(); + expect(mockRecordCollectedUsage).not.toHaveBeenCalled(); }); - it('should skip null entries in collectedUsage', async () => { - const collectedUsage = [ - { input_tokens: 100, output_tokens: 50, model: 'gpt-4' }, - null, - { input_tokens: 200, output_tokens: 60, model: 'gpt-4' }, - ]; - - await spendCollectedUsage({ - userId: 'user-123', - conversationId: 'convo-123', - collectedUsage, - fallbackModel: 'gpt-4', - }); - - expect(mockSpendTokens).toHaveBeenCalledTimes(2); - }); - - it('should spend tokens for single model', async () => { + it('should call recordCollectedUsage with abort context and full deps', async () => { const collectedUsage = [{ input_tokens: 100, output_tokens: 50, model: 'gpt-4' }]; await spendCollectedUsage({ @@ -186,21 +111,35 @@ describe('abortMiddleware - spendCollectedUsage', () => { conversationId: 'convo-123', collectedUsage, fallbackModel: 'gpt-4', + messageId: 'msg-123', }); - expect(mockSpendTokens).toHaveBeenCalledTimes(1); - expect(mockSpendTokens).toHaveBeenCalledWith( - expect.objectContaining({ - context: 'abort', - conversationId: 'convo-123', + expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1); + expect(mockRecordCollectedUsage).toHaveBeenCalledWith( + { + spendTokens: expect.any(Function), + spendStructuredTokens: expect.any(Function), + pricing: { + getMultiplier: mockGetMultiplier, + getCacheMultiplier: mockGetCacheMultiplier, + }, + bulkWriteOps: { + insertMany: mockBulkInsertTransactions, + updateBalance: mockUpdateBalance, + }, + }, + { user: 'user-123', + conversationId: 'convo-123', + collectedUsage, + context: 'abort', + messageId: 'msg-123', model: 'gpt-4', - }), - { promptTokens: 100, completionTokens: 50 }, + }, ); }); - it('should spend tokens for multiple models (parallel agents)', async () => { + it('should pass context abort for multiple models (parallel agents)', async () => { const collectedUsage = [ { input_tokens: 100, output_tokens: 50, model: 'gpt-4' }, { input_tokens: 80, output_tokens: 40, model: 'claude-3' }, @@ -214,136 +153,17 @@ describe('abortMiddleware - spendCollectedUsage', () => { fallbackModel: 'gpt-4', }); - expect(mockSpendTokens).toHaveBeenCalledTimes(3); - - // Verify each model was called - expect(mockSpendTokens).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ model: 'gpt-4' }), - { promptTokens: 100, completionTokens: 50 }, - ); - expect(mockSpendTokens).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ model: 'claude-3' }), - { promptTokens: 80, completionTokens: 40 }, - ); - expect(mockSpendTokens).toHaveBeenNthCalledWith( - 3, - expect.objectContaining({ model: 'gemini-pro' }), - { promptTokens: 120, completionTokens: 60 }, - ); - }); - - it('should use fallbackModel when usage.model is missing', async () => { - const collectedUsage = [{ input_tokens: 100, output_tokens: 50 }]; - - await spendCollectedUsage({ - userId: 'user-123', - conversationId: 'convo-123', - collectedUsage, - fallbackModel: 'fallback-model', - }); - - expect(mockSpendTokens).toHaveBeenCalledWith( - expect.objectContaining({ model: 'fallback-model' }), + expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1); + expect(mockRecordCollectedUsage).toHaveBeenCalledWith( expect.any(Object), + expect.objectContaining({ + context: 'abort', + collectedUsage, + }), ); }); - it('should use spendStructuredTokens for OpenAI format cache tokens', async () => { - const collectedUsage = [ - { - input_tokens: 100, - output_tokens: 50, - model: 'gpt-4', - input_token_details: { - cache_creation: 20, - cache_read: 10, - }, - }, - ]; - - await spendCollectedUsage({ - userId: 'user-123', - conversationId: 'convo-123', - collectedUsage, - fallbackModel: 'gpt-4', - }); - - expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1); - expect(mockSpendTokens).not.toHaveBeenCalled(); - expect(mockSpendStructuredTokens).toHaveBeenCalledWith( - expect.objectContaining({ model: 'gpt-4', context: 'abort' }), - { - promptTokens: { - input: 100, - write: 20, - read: 10, - }, - completionTokens: 50, - }, - ); - }); - - it('should use spendStructuredTokens for Anthropic format cache tokens', async () => { - const collectedUsage = [ - { - input_tokens: 100, - output_tokens: 50, - model: 'claude-3', - cache_creation_input_tokens: 25, - cache_read_input_tokens: 15, - }, - ]; - - await spendCollectedUsage({ - userId: 'user-123', - conversationId: 'convo-123', - collectedUsage, - fallbackModel: 'claude-3', - }); - - expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1); - expect(mockSpendTokens).not.toHaveBeenCalled(); - expect(mockSpendStructuredTokens).toHaveBeenCalledWith( - expect.objectContaining({ model: 'claude-3' }), - { - promptTokens: { - input: 100, - write: 25, - read: 15, - }, - completionTokens: 50, - }, - ); - }); - - it('should handle mixed cache and non-cache entries', async () => { - const collectedUsage = [ - { input_tokens: 100, output_tokens: 50, model: 'gpt-4' }, - { - input_tokens: 150, - output_tokens: 30, - model: 'claude-3', - cache_creation_input_tokens: 20, - cache_read_input_tokens: 10, - }, - { input_tokens: 200, output_tokens: 20, model: 'gemini-pro' }, - ]; - - await spendCollectedUsage({ - userId: 'user-123', - conversationId: 'convo-123', - collectedUsage, - fallbackModel: 'gpt-4', - }); - - expect(mockSpendTokens).toHaveBeenCalledTimes(2); - expect(mockSpendStructuredTokens).toHaveBeenCalledTimes(1); - }); - it('should handle real-world parallel agent abort scenario', async () => { - // Simulates: Primary agent (gemini) + addedConvo agent (gpt-5) aborted mid-stream const collectedUsage = [ { input_tokens: 31596, output_tokens: 151, model: 'gemini-3-flash-preview' }, { input_tokens: 28000, output_tokens: 120, model: 'gpt-5.2' }, @@ -356,27 +176,24 @@ describe('abortMiddleware - spendCollectedUsage', () => { fallbackModel: 'gemini-3-flash-preview', }); - expect(mockSpendTokens).toHaveBeenCalledTimes(2); - - // Primary model - expect(mockSpendTokens).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ model: 'gemini-3-flash-preview' }), - { promptTokens: 31596, completionTokens: 151 }, - ); - - // Parallel model (addedConvo) - expect(mockSpendTokens).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ model: 'gpt-5.2' }), - { promptTokens: 28000, completionTokens: 120 }, + expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1); + expect(mockRecordCollectedUsage).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ + user: 'user-123', + conversationId: 'convo-123', + context: 'abort', + model: 'gemini-3-flash-preview', + }), ); }); + /** + * Race condition prevention: after abort middleware spends tokens, + * the collectedUsage array is cleared so AgentClient.recordCollectedUsage() + * (which shares the same array reference) sees an empty array and returns early. + */ it('should clear collectedUsage array after spending to prevent double-spending', async () => { - // This tests the race condition fix: after abort middleware spends tokens, - // the collectedUsage array is cleared so AgentClient.recordCollectedUsage() - // (which shares the same array reference) sees an empty array and returns early. const collectedUsage = [ { input_tokens: 100, output_tokens: 50, model: 'gpt-4' }, { input_tokens: 80, output_tokens: 40, model: 'claude-3' }, @@ -391,19 +208,16 @@ describe('abortMiddleware - spendCollectedUsage', () => { fallbackModel: 'gpt-4', }); - expect(mockSpendTokens).toHaveBeenCalledTimes(2); - - // The array should be cleared after spending + expect(mockRecordCollectedUsage).toHaveBeenCalledTimes(1); expect(collectedUsage.length).toBe(0); }); - it('should await all token spending operations before clearing array', async () => { - // Ensure we don't clear the array before spending completes - let spendCallCount = 0; - mockSpendTokens.mockImplementation(async () => { - spendCallCount++; - // Simulate async delay + it('should await recordCollectedUsage before clearing array', async () => { + let resolved = false; + mockRecordCollectedUsage.mockImplementation(async () => { await new Promise((resolve) => setTimeout(resolve, 10)); + resolved = true; + return { input_tokens: 100, output_tokens: 50 }; }); const collectedUsage = [ @@ -418,10 +232,7 @@ describe('abortMiddleware - spendCollectedUsage', () => { fallbackModel: 'gpt-4', }); - // Both spend calls should have completed - expect(spendCallCount).toBe(2); - - // Array should be cleared after awaiting + expect(resolved).toBe(true); expect(collectedUsage.length).toBe(0); }); }); diff --git a/api/server/middleware/abortRun.js b/api/server/middleware/abortRun.js index 44375f5024..318693fe15 100644 --- a/api/server/middleware/abortRun.js +++ b/api/server/middleware/abortRun.js @@ -3,8 +3,7 @@ const { logger } = require('@librechat/data-schemas'); const { CacheKeys, RunStatus, isUUID } = require('librechat-data-provider'); const { initializeClient } = require('~/server/services/Endpoints/assistants'); const { checkMessageGaps, recordUsage } = require('~/server/services/Threads'); -const { deleteMessages } = require('~/models/Message'); -const { getConvo } = require('~/models/Conversation'); +const { deleteMessages, getConvo } = require('~/models'); const getLogStores = require('~/cache/getLogStores'); const three_minutes = 1000 * 60 * 3; diff --git a/api/server/middleware/accessResources/canAccessAgentFromBody.js b/api/server/middleware/accessResources/canAccessAgentFromBody.js index f8112af14d..5ade76bb77 100644 --- a/api/server/middleware/accessResources/canAccessAgentFromBody.js +++ b/api/server/middleware/accessResources/canAccessAgentFromBody.js @@ -1,42 +1,145 @@ const { logger } = require('@librechat/data-schemas'); const { Constants, + Permissions, ResourceType, + SystemRoles, + PermissionTypes, isAgentsEndpoint, isEphemeralAgentId, } = require('librechat-data-provider'); +const { checkPermission } = require('~/server/services/PermissionService'); const { canAccessResource } = require('./canAccessResource'); -const { getAgent } = require('~/models/Agent'); +const db = require('~/models'); + +const { getRoleByName, getAgent } = db; /** - * Agent ID resolver function for agent_id from request body - * Resolves custom agent ID (e.g., "agent_abc123") to MongoDB ObjectId - * This is used specifically for chat routes where agent_id comes from request body - * + * Resolves custom agent ID (e.g., "agent_abc123") to a MongoDB document. * @param {string} agentCustomId - Custom agent ID from request body - * @returns {Promise} Agent document with _id field, or null if not found + * @returns {Promise} Agent document with _id field, or null if ephemeral/not found */ const resolveAgentIdFromBody = async (agentCustomId) => { - // Handle ephemeral agents - they don't need permission checks - // Real agent IDs always start with "agent_", so anything else is ephemeral if (isEphemeralAgentId(agentCustomId)) { - return null; // No permission check needed for ephemeral agents + return null; } - - return await getAgent({ id: agentCustomId }); + return getAgent({ id: agentCustomId }); }; /** - * Middleware factory that creates middleware to check agent access permissions from request body. - * This middleware is specifically designed for chat routes where the agent_id comes from req.body - * instead of route parameters. + * Creates a `canAccessResource` middleware for the given agent ID + * and chains to the provided continuation on success. + * + * @param {string} agentId - The agent's custom string ID (e.g., "agent_abc123") + * @param {number} requiredPermission - Permission bit(s) required + * @param {import('express').Request} req + * @param {import('express').Response} res - Written on deny; continuation called on allow + * @param {Function} continuation - Called when the permission check passes + * @returns {Promise} + */ +const checkAgentResourceAccess = (agentId, requiredPermission, req, res, continuation) => { + const middleware = canAccessResource({ + resourceType: ResourceType.AGENT, + requiredPermission, + resourceIdParam: 'agent_id', + idResolver: () => resolveAgentIdFromBody(agentId), + }); + + const tempReq = { + ...req, + params: { ...req.params, agent_id: agentId }, + }; + + return middleware(tempReq, res, continuation); +}; + +/** + * Middleware factory that validates MULTI_CONVO:USE role permission and, when + * addedConvo.agent_id is a non-ephemeral agent, the same resource-level permission + * required for the primary agent (`requiredPermission`). Caches the resolved agent + * document on `req.resolvedAddedAgent` to avoid a duplicate DB fetch in `loadAddedAgent`. + * + * @param {number} requiredPermission - Permission bit(s) to check on the added agent resource + * @returns {(req: import('express').Request, res: import('express').Response, next: Function) => Promise} + */ +const checkAddedConvoAccess = (requiredPermission) => async (req, res, next) => { + const addedConvo = req.body?.addedConvo; + if (!addedConvo || typeof addedConvo !== 'object' || Array.isArray(addedConvo)) { + return next(); + } + + try { + if (!req.user?.role) { + return res.status(403).json({ + error: 'Forbidden', + message: 'Insufficient permissions for multi-conversation', + }); + } + + if (req.user.role !== SystemRoles.ADMIN) { + const role = await getRoleByName(req.user.role); + const hasMultiConvo = role?.permissions?.[PermissionTypes.MULTI_CONVO]?.[Permissions.USE]; + if (!hasMultiConvo) { + return res.status(403).json({ + error: 'Forbidden', + message: 'Multi-conversation feature is not enabled', + }); + } + } + + const addedAgentId = addedConvo.agent_id; + if (!addedAgentId || typeof addedAgentId !== 'string' || isEphemeralAgentId(addedAgentId)) { + return next(); + } + + if (req.user.role === SystemRoles.ADMIN) { + return next(); + } + + const agent = await resolveAgentIdFromBody(addedAgentId); + if (!agent) { + return res.status(404).json({ + error: 'Not Found', + message: `${ResourceType.AGENT} not found`, + }); + } + + const hasPermission = await checkPermission({ + userId: req.user.id, + role: req.user.role, + resourceType: ResourceType.AGENT, + resourceId: agent._id, + requiredPermission, + }); + + if (!hasPermission) { + return res.status(403).json({ + error: 'Forbidden', + message: `Insufficient permissions to access this ${ResourceType.AGENT}`, + }); + } + + req.resolvedAddedAgent = agent; + return next(); + } catch (error) { + logger.error('Failed to validate addedConvo access permissions', error); + return res.status(500).json({ + error: 'Internal Server Error', + message: 'Failed to validate addedConvo access permissions', + }); + } +}; + +/** + * Middleware factory that checks agent access permissions from request body. + * Validates both the primary agent_id and, when present, addedConvo.agent_id + * (which also requires MULTI_CONVO:USE role permission). * * @param {Object} options - Configuration options * @param {number} options.requiredPermission - The permission bit required (1=view, 2=edit, 4=delete, 8=share) * @returns {Function} Express middleware function * * @example - * // Basic usage for agent chat (requires VIEW permission) * router.post('/chat', * canAccessAgentFromBody({ requiredPermission: PermissionBits.VIEW }), * buildEndpointOption, @@ -46,11 +149,12 @@ const resolveAgentIdFromBody = async (agentCustomId) => { const canAccessAgentFromBody = (options) => { const { requiredPermission } = options; - // Validate required options if (!requiredPermission || typeof requiredPermission !== 'number') { throw new Error('canAccessAgentFromBody: requiredPermission is required and must be a number'); } + const addedConvoMiddleware = checkAddedConvoAccess(requiredPermission); + return async (req, res, next) => { try { const { endpoint, agent_id } = req.body; @@ -67,28 +171,13 @@ const canAccessAgentFromBody = (options) => { }); } - // Skip permission checks for ephemeral agents - // Real agent IDs always start with "agent_", so anything else is ephemeral + const afterPrimaryCheck = () => addedConvoMiddleware(req, res, next); + if (isEphemeralAgentId(agentId)) { - return next(); + return afterPrimaryCheck(); } - const agentAccessMiddleware = canAccessResource({ - resourceType: ResourceType.AGENT, - requiredPermission, - resourceIdParam: 'agent_id', // This will be ignored since we use custom resolver - idResolver: () => resolveAgentIdFromBody(agentId), - }); - - const tempReq = { - ...req, - params: { - ...req.params, - agent_id: agentId, - }, - }; - - return agentAccessMiddleware(tempReq, res, next); + return checkAgentResourceAccess(agentId, requiredPermission, req, res, afterPrimaryCheck); } catch (error) { logger.error('Failed to validate agent access permissions', error); return res.status(500).json({ diff --git a/api/server/middleware/accessResources/canAccessAgentFromBody.spec.js b/api/server/middleware/accessResources/canAccessAgentFromBody.spec.js new file mode 100644 index 0000000000..9e5e0b093a --- /dev/null +++ b/api/server/middleware/accessResources/canAccessAgentFromBody.spec.js @@ -0,0 +1,509 @@ +const mongoose = require('mongoose'); +const { + ResourceType, + SystemRoles, + PrincipalType, + PrincipalModel, +} = require('librechat-data-provider'); +const { MongoMemoryServer } = require('mongodb-memory-server'); +const { canAccessAgentFromBody } = require('./canAccessAgentFromBody'); +const { User, Role, AclEntry } = require('~/db/models'); +const { createAgent } = require('~/models'); + +describe('canAccessAgentFromBody middleware', () => { + let mongoServer; + let req, res, next; + let testUser, otherUser; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + await mongoose.connect(mongoServer.getUri()); + }); + + afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); + }); + + beforeEach(async () => { + await mongoose.connection.dropDatabase(); + + await Role.create({ + name: 'test-role', + permissions: { + AGENTS: { USE: true, CREATE: true, SHARE: true }, + MULTI_CONVO: { USE: true }, + }, + }); + + await Role.create({ + name: 'no-multi-convo', + permissions: { + AGENTS: { USE: true, CREATE: true, SHARE: true }, + MULTI_CONVO: { USE: false }, + }, + }); + + await Role.create({ + name: SystemRoles.ADMIN, + permissions: { + AGENTS: { USE: true, CREATE: true, SHARE: true }, + MULTI_CONVO: { USE: true }, + }, + }); + + testUser = await User.create({ + email: 'test@example.com', + name: 'Test User', + username: 'testuser', + role: 'test-role', + }); + + otherUser = await User.create({ + email: 'other@example.com', + name: 'Other User', + username: 'otheruser', + role: 'test-role', + }); + + req = { + user: { id: testUser._id, role: testUser.role }, + params: {}, + body: { + endpoint: 'agents', + agent_id: 'ephemeral_primary', + }, + }; + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + next = jest.fn(); + + jest.clearAllMocks(); + }); + + describe('middleware factory', () => { + test('throws if requiredPermission is missing', () => { + expect(() => canAccessAgentFromBody({})).toThrow( + 'canAccessAgentFromBody: requiredPermission is required and must be a number', + ); + }); + + test('throws if requiredPermission is not a number', () => { + expect(() => canAccessAgentFromBody({ requiredPermission: '1' })).toThrow( + 'canAccessAgentFromBody: requiredPermission is required and must be a number', + ); + }); + + test('returns a middleware function', () => { + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + expect(typeof middleware).toBe('function'); + expect(middleware.length).toBe(3); + }); + }); + + describe('primary agent checks', () => { + test('returns 400 when agent_id is missing on agents endpoint', async () => { + req.body.agent_id = undefined; + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(400); + }); + + test('proceeds for ephemeral primary agent without addedConvo', async () => { + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + + test('proceeds for non-agents endpoint (ephemeral fallback)', async () => { + req.body.endpoint = 'openAI'; + req.body.agent_id = undefined; + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + }); + }); + + describe('addedConvo — absent or invalid shape', () => { + test('calls next when addedConvo is absent', async () => { + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + }); + + test('calls next when addedConvo is a string', async () => { + req.body.addedConvo = 'not-an-object'; + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + }); + + test('calls next when addedConvo is an array', async () => { + req.body.addedConvo = [{ agent_id: 'agent_something' }]; + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + }); + }); + + describe('addedConvo — MULTI_CONVO permission gate', () => { + test('returns 403 when user lacks MULTI_CONVO:USE', async () => { + req.user.role = 'no-multi-convo'; + req.body.addedConvo = { agent_id: 'agent_x', endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + expect(res.json).toHaveBeenCalledWith( + expect.objectContaining({ message: 'Multi-conversation feature is not enabled' }), + ); + }); + + test('returns 403 when user.role is missing', async () => { + req.user = { id: testUser._id }; + req.body.addedConvo = { agent_id: 'agent_x', endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + }); + + test('ADMIN bypasses MULTI_CONVO check', async () => { + req.user.role = SystemRoles.ADMIN; + req.body.addedConvo = { agent_id: 'ephemeral_x', endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + }); + + describe('addedConvo — agent_id shape validation', () => { + test('calls next when agent_id is ephemeral', async () => { + req.body.addedConvo = { agent_id: 'ephemeral_xyz', endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + }); + + test('calls next when agent_id is absent', async () => { + req.body.addedConvo = { endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + }); + + test('calls next when agent_id is not a string (object injection)', async () => { + req.body.addedConvo = { agent_id: { $gt: '' }, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + }); + }); + + describe('addedConvo — agent resource ACL (IDOR prevention)', () => { + let addedAgent; + + beforeEach(async () => { + addedAgent = await createAgent({ + id: `agent_added_${Date.now()}`, + name: 'Private Agent', + provider: 'openai', + model: 'gpt-4', + author: otherUser._id, + }); + + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: otherUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: addedAgent._id, + permBits: 15, + grantedBy: otherUser._id, + }); + }); + + test('returns 403 when requester has no ACL for the added agent', async () => { + req.body.addedConvo = { agent_id: addedAgent.id, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + expect(res.json).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'Insufficient permissions to access this agent', + }), + ); + }); + + test('returns 404 when added agent does not exist', async () => { + req.body.addedConvo = { + agent_id: 'agent_nonexistent_999', + endpoint: 'agents', + model: 'gpt-4', + }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(404); + }); + + test('proceeds when requester has ACL for the added agent', async () => { + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: testUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: addedAgent._id, + permBits: 1, + grantedBy: otherUser._id, + }); + + req.body.addedConvo = { agent_id: addedAgent.id, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + + test('denies when ACL permission bits are insufficient', async () => { + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: testUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: addedAgent._id, + permBits: 1, + grantedBy: otherUser._id, + }); + + req.body.addedConvo = { agent_id: addedAgent.id, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 2 }); + await middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + }); + + test('caches resolved agent on req.resolvedAddedAgent', async () => { + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: testUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: addedAgent._id, + permBits: 1, + grantedBy: otherUser._id, + }); + + req.body.addedConvo = { agent_id: addedAgent.id, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(req.resolvedAddedAgent).toBeDefined(); + expect(req.resolvedAddedAgent._id.toString()).toBe(addedAgent._id.toString()); + }); + + test('ADMIN bypasses agent resource ACL for addedConvo', async () => { + req.user.role = SystemRoles.ADMIN; + req.body.addedConvo = { agent_id: addedAgent.id, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + expect(req.resolvedAddedAgent).toBeUndefined(); + }); + }); + + describe('end-to-end: primary real agent + addedConvo real agent', () => { + let primaryAgent, addedAgent; + + beforeEach(async () => { + primaryAgent = await createAgent({ + id: `agent_primary_${Date.now()}`, + name: 'Primary Agent', + provider: 'openai', + model: 'gpt-4', + author: testUser._id, + }); + + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: testUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: primaryAgent._id, + permBits: 15, + grantedBy: testUser._id, + }); + + addedAgent = await createAgent({ + id: `agent_added_${Date.now()}`, + name: 'Added Agent', + provider: 'openai', + model: 'gpt-4', + author: otherUser._id, + }); + + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: otherUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: addedAgent._id, + permBits: 15, + grantedBy: otherUser._id, + }); + + req.body.agent_id = primaryAgent.id; + }); + + test('both checks pass when user has ACL for both agents', async () => { + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: testUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: addedAgent._id, + permBits: 1, + grantedBy: otherUser._id, + }); + + req.body.addedConvo = { agent_id: addedAgent.id, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + expect(req.resolvedAddedAgent).toBeDefined(); + }); + + test('primary passes but addedConvo denied → 403', async () => { + req.body.addedConvo = { agent_id: addedAgent.id, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + }); + + test('primary denied → 403 without reaching addedConvo check', async () => { + const foreignAgent = await createAgent({ + id: `agent_foreign_${Date.now()}`, + name: 'Foreign Agent', + provider: 'openai', + model: 'gpt-4', + author: otherUser._id, + }); + + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: otherUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: foreignAgent._id, + permBits: 15, + grantedBy: otherUser._id, + }); + + req.body.agent_id = foreignAgent.id; + req.body.addedConvo = { agent_id: addedAgent.id, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + }); + }); + + describe('ephemeral primary + real addedConvo agent', () => { + let addedAgent; + + beforeEach(async () => { + addedAgent = await createAgent({ + id: `agent_added_${Date.now()}`, + name: 'Added Agent', + provider: 'openai', + model: 'gpt-4', + author: otherUser._id, + }); + + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: otherUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: addedAgent._id, + permBits: 15, + grantedBy: otherUser._id, + }); + }); + + test('runs full addedConvo ACL check even when primary is ephemeral', async () => { + req.body.addedConvo = { agent_id: addedAgent.id, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + }); + + test('proceeds when user has ACL for added agent (ephemeral primary)', async () => { + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: testUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: addedAgent._id, + permBits: 1, + grantedBy: otherUser._id, + }); + + req.body.addedConvo = { agent_id: addedAgent.id, endpoint: 'agents', model: 'gpt-4' }; + + const middleware = canAccessAgentFromBody({ requiredPermission: 1 }); + await middleware(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/api/server/middleware/accessResources/canAccessAgentResource.js b/api/server/middleware/accessResources/canAccessAgentResource.js index 62d9f248c0..4c00ab4982 100644 --- a/api/server/middleware/accessResources/canAccessAgentResource.js +++ b/api/server/middleware/accessResources/canAccessAgentResource.js @@ -1,6 +1,6 @@ const { ResourceType } = require('librechat-data-provider'); const { canAccessResource } = require('./canAccessResource'); -const { getAgent } = require('~/models/Agent'); +const { getAgent } = require('~/models'); /** * Agent ID resolver function diff --git a/api/server/middleware/accessResources/canAccessAgentResource.spec.js b/api/server/middleware/accessResources/canAccessAgentResource.spec.js index 1106390e72..786636ee74 100644 --- a/api/server/middleware/accessResources/canAccessAgentResource.spec.js +++ b/api/server/middleware/accessResources/canAccessAgentResource.spec.js @@ -3,7 +3,7 @@ const { ResourceType, PrincipalType, PrincipalModel } = require('librechat-data- const { MongoMemoryServer } = require('mongodb-memory-server'); const { canAccessAgentResource } = require('./canAccessAgentResource'); const { User, Role, AclEntry } = require('~/db/models'); -const { createAgent } = require('~/models/Agent'); +const { createAgent } = require('~/models'); describe('canAccessAgentResource middleware', () => { let mongoServer; @@ -373,7 +373,7 @@ describe('canAccessAgentResource middleware', () => { jest.clearAllMocks(); // Update the agent - const { updateAgent } = require('~/models/Agent'); + const { updateAgent } = require('~/models'); await updateAgent({ id: agentId }, { description: 'Updated description' }); // Test edit access diff --git a/api/server/middleware/accessResources/canAccessMCPServerResource.spec.js b/api/server/middleware/accessResources/canAccessMCPServerResource.spec.js index 77508be2d1..6f7e4ab506 100644 --- a/api/server/middleware/accessResources/canAccessMCPServerResource.spec.js +++ b/api/server/middleware/accessResources/canAccessMCPServerResource.spec.js @@ -1,8 +1,9 @@ const mongoose = require('mongoose'); const { ResourceType, PrincipalType, PrincipalModel } = require('librechat-data-provider'); +const { SystemCapabilities } = require('@librechat/data-schemas'); const { MongoMemoryServer } = require('mongodb-memory-server'); const { canAccessMCPServerResource } = require('./canAccessMCPServerResource'); -const { User, Role, AclEntry } = require('~/db/models'); +const { User, Role, AclEntry, SystemGrant } = require('~/db/models'); const { createMCPServer } = require('~/models'); describe('canAccessMCPServerResource middleware', () => { @@ -511,7 +512,7 @@ describe('canAccessMCPServerResource middleware', () => { }); }); - test('should allow admin users to bypass permission checks', async () => { + test('should allow users with MANAGE_MCP_SERVERS capability to bypass permission checks', async () => { const { SystemRoles } = require('librechat-data-provider'); // Create an MCP server owned by another user @@ -531,6 +532,14 @@ describe('canAccessMCPServerResource middleware', () => { author: otherUser._id, }); + // Seed MANAGE_MCP_SERVERS capability for the ADMIN role + await SystemGrant.create({ + principalType: PrincipalType.ROLE, + principalId: SystemRoles.ADMIN, + capability: SystemCapabilities.MANAGE_MCP_SERVERS, + grantedAt: new Date(), + }); + // Set user as admin req.user = { id: testUser._id, role: SystemRoles.ADMIN }; req.params.serverName = mcpServer.serverName; diff --git a/api/server/middleware/accessResources/canAccessPromptGroupResource.js b/api/server/middleware/accessResources/canAccessPromptGroupResource.js index 90aa280772..9da1994a77 100644 --- a/api/server/middleware/accessResources/canAccessPromptGroupResource.js +++ b/api/server/middleware/accessResources/canAccessPromptGroupResource.js @@ -1,6 +1,6 @@ const { ResourceType } = require('librechat-data-provider'); const { canAccessResource } = require('./canAccessResource'); -const { getPromptGroup } = require('~/models/Prompt'); +const { getPromptGroup } = require('~/models'); /** * PromptGroup ID resolver function diff --git a/api/server/middleware/accessResources/canAccessPromptViaGroup.js b/api/server/middleware/accessResources/canAccessPromptViaGroup.js index 0bb0a804a9..534db3d6c6 100644 --- a/api/server/middleware/accessResources/canAccessPromptViaGroup.js +++ b/api/server/middleware/accessResources/canAccessPromptViaGroup.js @@ -1,6 +1,6 @@ const { ResourceType } = require('librechat-data-provider'); const { canAccessResource } = require('./canAccessResource'); -const { getPrompt } = require('~/models/Prompt'); +const { getPrompt } = require('~/models'); /** * Prompt to PromptGroup ID resolver function diff --git a/api/server/middleware/accessResources/canAccessResource.js b/api/server/middleware/accessResources/canAccessResource.js index c8bd15ffc2..2431971b2f 100644 --- a/api/server/middleware/accessResources/canAccessResource.js +++ b/api/server/middleware/accessResources/canAccessResource.js @@ -1,5 +1,5 @@ -const { logger } = require('@librechat/data-schemas'); -const { SystemRoles } = require('librechat-data-provider'); +const { logger, ResourceCapabilityMap } = require('@librechat/data-schemas'); +const { hasCapability } = require('~/server/middleware/roles/capabilities'); const { checkPermission } = require('~/server/services/PermissionService'); /** @@ -71,8 +71,17 @@ const canAccessResource = (options) => { message: 'Authentication required', }); } - // if system admin let through - if (req.user.role === SystemRoles.ADMIN) { + const cap = ResourceCapabilityMap[resourceType]; + let hasCap = false; + try { + hasCap = cap != null && (await hasCapability(req.user, cap)); + } catch (err) { + logger.warn(`[canAccessResource] capability check failed, denying bypass: ${err.message}`); + } + if (hasCap) { + logger.debug( + `[canAccessResource] ${cap} bypass for user ${req.user.id} on ${resourceType} ${rawResourceId}`, + ); return next(); } const userId = req.user.id; diff --git a/api/server/middleware/accessResources/fileAccess.js b/api/server/middleware/accessResources/fileAccess.js index 25d41e7c02..0f77a61175 100644 --- a/api/server/middleware/accessResources/fileAccess.js +++ b/api/server/middleware/accessResources/fileAccess.js @@ -1,8 +1,7 @@ const { logger } = require('@librechat/data-schemas'); const { PermissionBits, hasPermissions, ResourceType } = require('librechat-data-provider'); const { getEffectivePermissions } = require('~/server/services/PermissionService'); -const { getAgents } = require('~/models/Agent'); -const { getFiles } = require('~/models'); +const { getAgents, getFiles } = require('~/models'); /** * Checks if user has access to a file through agent permissions diff --git a/api/server/middleware/accessResources/fileAccess.spec.js b/api/server/middleware/accessResources/fileAccess.spec.js index cc0d57ac48..72896b0629 100644 --- a/api/server/middleware/accessResources/fileAccess.spec.js +++ b/api/server/middleware/accessResources/fileAccess.spec.js @@ -3,8 +3,7 @@ const { ResourceType, PrincipalType, PrincipalModel } = require('librechat-data- const { MongoMemoryServer } = require('mongodb-memory-server'); const { fileAccess } = require('./fileAccess'); const { User, Role, AclEntry } = require('~/db/models'); -const { createAgent } = require('~/models/Agent'); -const { createFile } = require('~/models'); +const { createAgent, createFile } = require('~/models'); describe('fileAccess middleware', () => { let mongoServer; diff --git a/api/server/middleware/assistants/validateAuthor.js b/api/server/middleware/assistants/validateAuthor.js index 03936444e0..3be1642a71 100644 --- a/api/server/middleware/assistants/validateAuthor.js +++ b/api/server/middleware/assistants/validateAuthor.js @@ -1,5 +1,6 @@ -const { SystemRoles } = require('librechat-data-provider'); -const { getAssistant } = require('~/models/Assistant'); +const { logger, SystemCapabilities } = require('@librechat/data-schemas'); +const { hasCapability } = require('~/server/middleware'); +const { getAssistant } = require('~/models'); /** * Checks if the assistant is supported or excluded @@ -12,10 +13,6 @@ const { getAssistant } = require('~/models/Assistant'); * @returns {Promise} */ const validateAuthor = async ({ req, openai, overrideEndpoint, overrideAssistantId }) => { - if (req.user.role === SystemRoles.ADMIN) { - return; - } - const endpoint = overrideEndpoint ?? req.body.endpoint ?? req.query.endpoint; const assistant_id = overrideAssistantId ?? req.params.id ?? req.body.assistant_id ?? req.query.assistant_id; @@ -31,6 +28,18 @@ const validateAuthor = async ({ req, openai, overrideEndpoint, overrideAssistant return; } + let canManageAssistants = false; + try { + canManageAssistants = await hasCapability(req.user, SystemCapabilities.MANAGE_ASSISTANTS); + } catch (err) { + logger.warn(`[validateAuthor] capability check failed, denying bypass: ${err.message}`); + } + + if (canManageAssistants) { + logger.debug(`[validateAuthor] MANAGE_ASSISTANTS bypass for user ${req.user.id}`); + return; + } + const assistantDoc = await getAssistant({ assistant_id, user: req.user.id }); if (assistantDoc) { return; diff --git a/api/server/middleware/buildEndpointOption.js b/api/server/middleware/buildEndpointOption.js index f56d850120..64ed8e7466 100644 --- a/api/server/middleware/buildEndpointOption.js +++ b/api/server/middleware/buildEndpointOption.js @@ -5,9 +5,11 @@ const { EModelEndpoint, isAgentsEndpoint, parseCompactConvo, + getDefaultParamsEndpoint, } = require('librechat-data-provider'); const azureAssistants = require('~/server/services/Endpoints/azureAssistants'); const assistants = require('~/server/services/Endpoints/assistants'); +const { getEndpointsConfig } = require('~/server/services/Config'); const agents = require('~/server/services/Endpoints/agents'); const { updateFilesUsage } = require('~/models'); @@ -19,9 +21,24 @@ const buildFunction = { async function buildEndpointOption(req, res, next) { const { endpoint, endpointType } = req.body; + + let endpointsConfig; + try { + endpointsConfig = await getEndpointsConfig(req); + } catch (error) { + logger.error('Error fetching endpoints config in buildEndpointOption', error); + } + + const defaultParamsEndpoint = getDefaultParamsEndpoint(endpointsConfig, endpoint); + let parsedBody; try { - parsedBody = parseCompactConvo({ endpoint, endpointType, conversation: req.body }); + parsedBody = parseCompactConvo({ + endpoint, + endpointType, + conversation: req.body, + defaultParamsEndpoint, + }); } catch (error) { logger.error(`Error parsing compact conversation for endpoint ${endpoint}`, error); logger.debug({ @@ -55,6 +72,7 @@ async function buildEndpointOption(req, res, next) { endpoint, endpointType, conversation: currentModelSpec.preset, + defaultParamsEndpoint, }); if (currentModelSpec.iconURL != null && currentModelSpec.iconURL !== '') { parsedBody.iconURL = currentModelSpec.iconURL; diff --git a/api/server/middleware/buildEndpointOption.spec.js b/api/server/middleware/buildEndpointOption.spec.js new file mode 100644 index 0000000000..eab5e2666b --- /dev/null +++ b/api/server/middleware/buildEndpointOption.spec.js @@ -0,0 +1,237 @@ +/** + * Wrap parseCompactConvo: the REAL function runs, but jest can observe + * calls and return values. Must be declared before require('./buildEndpointOption') + * so the destructured reference in the middleware captures the wrapper. + */ +jest.mock('librechat-data-provider', () => { + const actual = jest.requireActual('librechat-data-provider'); + return { + ...actual, + parseCompactConvo: jest.fn((...args) => actual.parseCompactConvo(...args)), + }; +}); + +const { EModelEndpoint, parseCompactConvo } = require('librechat-data-provider'); + +const mockBuildOptions = jest.fn((_endpoint, parsedBody) => ({ + ...parsedBody, + endpoint: _endpoint, +})); + +jest.mock('~/server/services/Endpoints/azureAssistants', () => ({ + buildOptions: mockBuildOptions, +})); +jest.mock('~/server/services/Endpoints/assistants', () => ({ + buildOptions: mockBuildOptions, +})); +jest.mock('~/server/services/Endpoints/agents', () => ({ + buildOptions: mockBuildOptions, +})); + +jest.mock('~/models', () => ({ + updateFilesUsage: jest.fn(), +})); + +const mockGetEndpointsConfig = jest.fn(); +jest.mock('~/server/services/Config', () => ({ + getEndpointsConfig: (...args) => mockGetEndpointsConfig(...args), +})); + +jest.mock('@librechat/api', () => ({ + handleError: jest.fn(), +})); + +const buildEndpointOption = require('./buildEndpointOption'); + +const createReq = (body, config = {}) => ({ + body, + config, + baseUrl: '/api/chat', +}); + +const createRes = () => ({ + status: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnThis(), +}); + +describe('buildEndpointOption - defaultParamsEndpoint parsing', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should pass defaultParamsEndpoint to parseCompactConvo and preserve maxOutputTokens', async () => { + mockGetEndpointsConfig.mockResolvedValue({ + AnthropicClaude: { + type: EModelEndpoint.custom, + customParams: { + defaultParamsEndpoint: EModelEndpoint.anthropic, + }, + }, + }); + + const req = createReq( + { + endpoint: 'AnthropicClaude', + endpointType: EModelEndpoint.custom, + model: 'anthropic/claude-opus-4.5', + temperature: 0.7, + maxOutputTokens: 8192, + topP: 0.9, + maxContextTokens: 50000, + }, + { modelSpecs: null }, + ); + + await buildEndpointOption(req, createRes(), jest.fn()); + + expect(parseCompactConvo).toHaveBeenCalledWith( + expect.objectContaining({ + defaultParamsEndpoint: EModelEndpoint.anthropic, + }), + ); + + const parsedResult = parseCompactConvo.mock.results[0].value; + expect(parsedResult.maxOutputTokens).toBe(8192); + expect(parsedResult.topP).toBe(0.9); + expect(parsedResult.temperature).toBe(0.7); + expect(parsedResult.maxContextTokens).toBe(50000); + }); + + it('should strip maxOutputTokens when no defaultParamsEndpoint is configured', async () => { + mockGetEndpointsConfig.mockResolvedValue({ + MyOpenRouter: { + type: EModelEndpoint.custom, + }, + }); + + const req = createReq( + { + endpoint: 'MyOpenRouter', + endpointType: EModelEndpoint.custom, + model: 'gpt-4o', + temperature: 0.7, + maxOutputTokens: 8192, + max_tokens: 4096, + }, + { modelSpecs: null }, + ); + + await buildEndpointOption(req, createRes(), jest.fn()); + + expect(parseCompactConvo).toHaveBeenCalledWith( + expect.objectContaining({ + defaultParamsEndpoint: undefined, + }), + ); + + const parsedResult = parseCompactConvo.mock.results[0].value; + expect(parsedResult.maxOutputTokens).toBeUndefined(); + expect(parsedResult.max_tokens).toBe(4096); + expect(parsedResult.temperature).toBe(0.7); + }); + + it('should strip bedrock region from custom endpoint without defaultParamsEndpoint', async () => { + mockGetEndpointsConfig.mockResolvedValue({ + MyEndpoint: { + type: EModelEndpoint.custom, + }, + }); + + const req = createReq( + { + endpoint: 'MyEndpoint', + endpointType: EModelEndpoint.custom, + model: 'gpt-4o', + temperature: 0.7, + region: 'us-east-1', + }, + { modelSpecs: null }, + ); + + await buildEndpointOption(req, createRes(), jest.fn()); + + const parsedResult = parseCompactConvo.mock.results[0].value; + expect(parsedResult.region).toBeUndefined(); + expect(parsedResult.temperature).toBe(0.7); + }); + + it('should pass defaultParamsEndpoint when re-parsing enforced model spec', async () => { + mockGetEndpointsConfig.mockResolvedValue({ + AnthropicClaude: { + type: EModelEndpoint.custom, + customParams: { + defaultParamsEndpoint: EModelEndpoint.anthropic, + }, + }, + }); + + const modelSpec = { + name: 'claude-opus-4.5', + preset: { + endpoint: 'AnthropicClaude', + endpointType: EModelEndpoint.custom, + model: 'anthropic/claude-opus-4.5', + temperature: 0.7, + maxOutputTokens: 8192, + maxContextTokens: 50000, + }, + }; + + const req = createReq( + { + endpoint: 'AnthropicClaude', + endpointType: EModelEndpoint.custom, + spec: 'claude-opus-4.5', + model: 'anthropic/claude-opus-4.5', + }, + { + modelSpecs: { + enforce: true, + list: [modelSpec], + }, + }, + ); + + await buildEndpointOption(req, createRes(), jest.fn()); + + const enforcedCall = parseCompactConvo.mock.calls[1]; + expect(enforcedCall[0]).toEqual( + expect.objectContaining({ + defaultParamsEndpoint: EModelEndpoint.anthropic, + }), + ); + + const enforcedResult = parseCompactConvo.mock.results[1].value; + expect(enforcedResult.maxOutputTokens).toBe(8192); + expect(enforcedResult.temperature).toBe(0.7); + expect(enforcedResult.maxContextTokens).toBe(50000); + }); + + it('should fall back to OpenAI schema when getEndpointsConfig fails', async () => { + mockGetEndpointsConfig.mockRejectedValue(new Error('Config unavailable')); + + const req = createReq( + { + endpoint: 'AnthropicClaude', + endpointType: EModelEndpoint.custom, + model: 'anthropic/claude-opus-4.5', + temperature: 0.7, + maxOutputTokens: 8192, + max_tokens: 4096, + }, + { modelSpecs: null }, + ); + + await buildEndpointOption(req, createRes(), jest.fn()); + + expect(parseCompactConvo).toHaveBeenCalledWith( + expect.objectContaining({ + defaultParamsEndpoint: undefined, + }), + ); + + const parsedResult = parseCompactConvo.mock.results[0].value; + expect(parsedResult.maxOutputTokens).toBeUndefined(); + expect(parsedResult.max_tokens).toBe(4096); + }); +}); diff --git a/api/server/middleware/canDeleteAccount.js b/api/server/middleware/canDeleteAccount.js index a913495287..3c08745d76 100644 --- a/api/server/middleware/canDeleteAccount.js +++ b/api/server/middleware/canDeleteAccount.js @@ -1,6 +1,6 @@ const { isEnabled } = require('@librechat/api'); -const { logger } = require('@librechat/data-schemas'); -const { SystemRoles } = require('librechat-data-provider'); +const { logger, SystemCapabilities } = require('@librechat/data-schemas'); +const { hasCapability } = require('~/server/middleware/roles/capabilities'); /** * Checks if the user can delete their account @@ -17,12 +17,29 @@ const { SystemRoles } = require('librechat-data-provider'); const canDeleteAccount = async (req, res, next = () => {}) => { const { user } = req; const { ALLOW_ACCOUNT_DELETION = true } = process.env; - if (user?.role === SystemRoles.ADMIN || isEnabled(ALLOW_ACCOUNT_DELETION)) { + if (isEnabled(ALLOW_ACCOUNT_DELETION)) { return next(); - } else { - logger.error(`[User] [Delete Account] [User cannot delete account] [User: ${user?.id}]`); - return res.status(403).send({ message: 'You do not have permission to delete this account' }); } + let hasAdminAccess = false; + if (user) { + try { + const id = user.id ?? user._id?.toString(); + if (id) { + hasAdminAccess = await hasCapability( + { id, role: user.role ?? '', tenantId: user.tenantId }, + SystemCapabilities.ACCESS_ADMIN, + ); + } + } catch (err) { + logger.warn(`[canDeleteAccount] capability check failed, denying: ${err.message}`); + } + } + if (hasAdminAccess) { + logger.debug(`[canDeleteAccount] ACCESS_ADMIN bypass for user ${user.id}`); + return next(); + } + logger.error(`[User] [Delete Account] [User cannot delete account] [User: ${user?.id}]`); + return res.status(403).send({ message: 'You do not have permission to delete this account' }); }; module.exports = canDeleteAccount; diff --git a/api/server/middleware/canDeleteAccount.spec.js b/api/server/middleware/canDeleteAccount.spec.js new file mode 100644 index 0000000000..abb888c4a4 --- /dev/null +++ b/api/server/middleware/canDeleteAccount.spec.js @@ -0,0 +1,180 @@ +const mongoose = require('mongoose'); +const { MongoMemoryServer } = require('mongodb-memory-server'); +const { SystemRoles, PrincipalType } = require('librechat-data-provider'); +const { SystemCapabilities } = require('@librechat/data-schemas'); + +jest.mock('@librechat/data-schemas', () => ({ + ...jest.requireActual('@librechat/data-schemas'), + logger: { error: jest.fn(), warn: jest.fn(), debug: jest.fn(), info: jest.fn() }, +})); + +jest.mock('~/cache', () => ({ + getLogStores: jest.fn(() => ({ + get: jest.fn(), + set: jest.fn(), + })), +})); + +const { User, SystemGrant } = require('~/db/models'); +const canDeleteAccount = require('./canDeleteAccount'); + +let mongoServer; + +beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + await mongoose.connect(mongoServer.getUri()); +}); + +afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); +}); + +beforeEach(async () => { + await mongoose.connection.dropDatabase(); + delete process.env.ALLOW_ACCOUNT_DELETION; +}); + +const makeRes = () => { + const send = jest.fn(); + const status = jest.fn().mockReturnValue({ send }); + return { status, send }; +}; + +describe('canDeleteAccount', () => { + describe('ALLOW_ACCOUNT_DELETION=true (default)', () => { + it('calls next without hitting the DB', async () => { + process.env.ALLOW_ACCOUNT_DELETION = 'true'; + const next = jest.fn(); + const req = { user: { id: 'user-1', role: SystemRoles.USER } }; + + await canDeleteAccount(req, makeRes(), next); + + expect(next).toHaveBeenCalled(); + }); + + it('skips capability check entirely when deletion is allowed', async () => { + process.env.ALLOW_ACCOUNT_DELETION = 'true'; + const next = jest.fn(); + const req = { user: { id: 'user-1', role: SystemRoles.USER } }; + + await canDeleteAccount(req, makeRes(), next); + + expect(next).toHaveBeenCalled(); + const grantCount = await SystemGrant.countDocuments(); + expect(grantCount).toBe(0); + }); + }); + + describe('ALLOW_ACCOUNT_DELETION=false', () => { + beforeEach(() => { + process.env.ALLOW_ACCOUNT_DELETION = 'false'; + }); + + it('allows admin with ACCESS_ADMIN grant (real DB check)', async () => { + const admin = await User.create({ + name: 'Admin', + email: 'admin@test.com', + password: 'password123', + provider: 'local', + role: SystemRoles.ADMIN, + }); + + await SystemGrant.create({ + principalType: PrincipalType.ROLE, + principalId: SystemRoles.ADMIN, + capability: SystemCapabilities.ACCESS_ADMIN, + grantedAt: new Date(), + }); + + const next = jest.fn(); + const req = { user: { id: admin._id.toString(), role: SystemRoles.ADMIN } }; + + await canDeleteAccount(req, makeRes(), next); + + expect(next).toHaveBeenCalled(); + }); + + it('blocks regular user without ACCESS_ADMIN grant', async () => { + const user = await User.create({ + name: 'Regular', + email: 'user@test.com', + password: 'password123', + provider: 'local', + role: SystemRoles.USER, + }); + + const next = jest.fn(); + const res = makeRes(); + const req = { user: { id: user._id.toString(), role: SystemRoles.USER } }; + + await canDeleteAccount(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + }); + + it('blocks admin role WITHOUT the ACCESS_ADMIN grant', async () => { + const admin = await User.create({ + name: 'Admin No Grant', + email: 'admin2@test.com', + password: 'password123', + provider: 'local', + role: SystemRoles.ADMIN, + }); + + const next = jest.fn(); + const res = makeRes(); + const req = { user: { id: admin._id.toString(), role: SystemRoles.ADMIN } }; + + await canDeleteAccount(req, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + }); + + it('allows user-level grant (not just role-level)', async () => { + const user = await User.create({ + name: 'Privileged User', + email: 'priv@test.com', + password: 'password123', + provider: 'local', + role: SystemRoles.USER, + }); + + await SystemGrant.create({ + principalType: PrincipalType.USER, + principalId: user._id, + capability: SystemCapabilities.ACCESS_ADMIN, + grantedAt: new Date(), + }); + + const next = jest.fn(); + const req = { user: { id: user._id.toString(), role: SystemRoles.USER } }; + + await canDeleteAccount(req, makeRes(), next); + + expect(next).toHaveBeenCalled(); + }); + + it('blocks when user is undefined — does not throw', async () => { + const next = jest.fn(); + const res = makeRes(); + + await canDeleteAccount({ user: undefined }, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + }); + + it('blocks when user is null — does not throw', async () => { + const next = jest.fn(); + const res = makeRes(); + + await canDeleteAccount({ user: null }, res, next); + + expect(next).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(403); + }); + }); +}); diff --git a/api/server/middleware/checkInviteUser.js b/api/server/middleware/checkInviteUser.js index 42e1faba5b..22f2824ffc 100644 --- a/api/server/middleware/checkInviteUser.js +++ b/api/server/middleware/checkInviteUser.js @@ -1,5 +1,8 @@ -const { getInvite } = require('~/models/inviteUser'); -const { deleteTokens } = require('~/models'); +const { getInvite: getInviteFn } = require('@librechat/api'); +const { createToken, findToken, deleteTokens } = require('~/models'); + +const getInvite = (encodedToken, email) => + getInviteFn(encodedToken, email, { createToken, findToken }); async function checkInviteUser(req, res, next) { const token = req.body.token; diff --git a/api/server/middleware/checkPeoplePickerAccess.js b/api/server/middleware/checkPeoplePickerAccess.js index 0e604272db..50f137285e 100644 --- a/api/server/middleware/checkPeoplePickerAccess.js +++ b/api/server/middleware/checkPeoplePickerAccess.js @@ -1,14 +1,21 @@ const { logger } = require('@librechat/data-schemas'); const { PrincipalType, PermissionTypes, Permissions } = require('librechat-data-provider'); -const { getRoleByName } = require('~/models/Role'); +const { getRoleByName } = require('~/models'); + +const VALID_PRINCIPAL_TYPES = new Set([ + PrincipalType.USER, + PrincipalType.GROUP, + PrincipalType.ROLE, +]); /** - * Middleware to check if user has permission to access people picker functionality - * Checks specific permission based on the 'type' query parameter: - * - type=user: requires VIEW_USERS permission - * - type=group: requires VIEW_GROUPS permission - * - type=role: requires VIEW_ROLES permission - * - no type (mixed search): requires either VIEW_USERS OR VIEW_GROUPS OR VIEW_ROLES + * Middleware to check if user has permission to access people picker functionality. + * Validates requested principal types via `type` (singular) and `types` (comma-separated or array) + * query parameters against the caller's role permissions: + * - user: requires VIEW_USERS permission + * - group: requires VIEW_GROUPS permission + * - role: requires VIEW_ROLES permission + * - no type filter (mixed search): requires at least one of the above */ const checkPeoplePickerAccess = async (req, res, next) => { try { @@ -28,7 +35,7 @@ const checkPeoplePickerAccess = async (req, res, next) => { }); } - const { type } = req.query; + const { type, types } = req.query; const peoplePickerPerms = role.permissions[PermissionTypes.PEOPLE_PICKER] || {}; const canViewUsers = peoplePickerPerms[Permissions.VIEW_USERS] === true; const canViewGroups = peoplePickerPerms[Permissions.VIEW_GROUPS] === true; @@ -49,15 +56,32 @@ const checkPeoplePickerAccess = async (req, res, next) => { }, }; - const check = permissionChecks[type]; - if (check && !check.hasPermission) { - return res.status(403).json({ - error: 'Forbidden', - message: check.message, - }); + const requestedTypes = new Set(); + + if (type && VALID_PRINCIPAL_TYPES.has(type)) { + requestedTypes.add(type); } - if (!type && !canViewUsers && !canViewGroups && !canViewRoles) { + if (types) { + const typesArray = Array.isArray(types) ? types : types.split(','); + for (const t of typesArray) { + if (VALID_PRINCIPAL_TYPES.has(t)) { + requestedTypes.add(t); + } + } + } + + for (const requested of requestedTypes) { + const check = permissionChecks[requested]; + if (!check.hasPermission) { + return res.status(403).json({ + error: 'Forbidden', + message: check.message, + }); + } + } + + if (requestedTypes.size === 0 && !canViewUsers && !canViewGroups && !canViewRoles) { return res.status(403).json({ error: 'Forbidden', message: 'Insufficient permissions to search for users, groups, or roles', @@ -67,7 +91,7 @@ const checkPeoplePickerAccess = async (req, res, next) => { next(); } catch (error) { logger.error( - `[checkPeoplePickerAccess][${req.user?.id}] checkPeoplePickerAccess error for req.query.type = ${req.query.type}`, + `[checkPeoplePickerAccess][${req.user?.id}] error for type=${req.query.type}, types=${req.query.types}`, error, ); return res.status(500).json({ diff --git a/api/server/middleware/checkPeoplePickerAccess.spec.js b/api/server/middleware/checkPeoplePickerAccess.spec.js index 52bf0e6724..c394bbae65 100644 --- a/api/server/middleware/checkPeoplePickerAccess.spec.js +++ b/api/server/middleware/checkPeoplePickerAccess.spec.js @@ -1,9 +1,9 @@ const { logger } = require('@librechat/data-schemas'); const { PrincipalType, PermissionTypes, Permissions } = require('librechat-data-provider'); const { checkPeoplePickerAccess } = require('./checkPeoplePickerAccess'); -const { getRoleByName } = require('~/models/Role'); +const { getRoleByName } = require('~/models'); -jest.mock('~/models/Role'); +jest.mock('~/models'); jest.mock('@librechat/data-schemas', () => ({ ...jest.requireActual('@librechat/data-schemas'), logger: { @@ -173,6 +173,171 @@ describe('checkPeoplePickerAccess', () => { expect(next).not.toHaveBeenCalled(); }); + it('should deny access when using types param to bypass type-specific check', async () => { + req.query.types = PrincipalType.GROUP; + getRoleByName.mockResolvedValue({ + permissions: { + [PermissionTypes.PEOPLE_PICKER]: { + [Permissions.VIEW_USERS]: true, + [Permissions.VIEW_GROUPS]: false, + [Permissions.VIEW_ROLES]: false, + }, + }, + }); + + await checkPeoplePickerAccess(req, res, next); + + expect(res.status).toHaveBeenCalledWith(403); + expect(res.json).toHaveBeenCalledWith({ + error: 'Forbidden', + message: 'Insufficient permissions to search for groups', + }); + expect(next).not.toHaveBeenCalled(); + }); + + it('should deny access when types contains any unpermitted type', async () => { + req.query.types = `${PrincipalType.USER},${PrincipalType.ROLE}`; + getRoleByName.mockResolvedValue({ + permissions: { + [PermissionTypes.PEOPLE_PICKER]: { + [Permissions.VIEW_USERS]: true, + [Permissions.VIEW_GROUPS]: false, + [Permissions.VIEW_ROLES]: false, + }, + }, + }); + + await checkPeoplePickerAccess(req, res, next); + + expect(res.status).toHaveBeenCalledWith(403); + expect(res.json).toHaveBeenCalledWith({ + error: 'Forbidden', + message: 'Insufficient permissions to search for roles', + }); + expect(next).not.toHaveBeenCalled(); + }); + + it('should allow access when all requested types are permitted', async () => { + req.query.types = `${PrincipalType.USER},${PrincipalType.GROUP}`; + getRoleByName.mockResolvedValue({ + permissions: { + [PermissionTypes.PEOPLE_PICKER]: { + [Permissions.VIEW_USERS]: true, + [Permissions.VIEW_GROUPS]: true, + [Permissions.VIEW_ROLES]: false, + }, + }, + }); + + await checkPeoplePickerAccess(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + + it('should validate types when provided as array (Express qs parsing)', async () => { + req.query.types = [PrincipalType.GROUP, PrincipalType.ROLE]; + getRoleByName.mockResolvedValue({ + permissions: { + [PermissionTypes.PEOPLE_PICKER]: { + [Permissions.VIEW_USERS]: true, + [Permissions.VIEW_GROUPS]: false, + [Permissions.VIEW_ROLES]: true, + }, + }, + }); + + await checkPeoplePickerAccess(req, res, next); + + expect(res.status).toHaveBeenCalledWith(403); + expect(res.json).toHaveBeenCalledWith({ + error: 'Forbidden', + message: 'Insufficient permissions to search for groups', + }); + expect(next).not.toHaveBeenCalled(); + }); + + it('should enforce permissions for combined type and types params', async () => { + req.query.type = PrincipalType.USER; + req.query.types = PrincipalType.GROUP; + getRoleByName.mockResolvedValue({ + permissions: { + [PermissionTypes.PEOPLE_PICKER]: { + [Permissions.VIEW_USERS]: true, + [Permissions.VIEW_GROUPS]: false, + [Permissions.VIEW_ROLES]: false, + }, + }, + }); + + await checkPeoplePickerAccess(req, res, next); + + expect(res.status).toHaveBeenCalledWith(403); + expect(res.json).toHaveBeenCalledWith({ + error: 'Forbidden', + message: 'Insufficient permissions to search for groups', + }); + expect(next).not.toHaveBeenCalled(); + }); + + it('should treat all-invalid types values as mixed search', async () => { + req.query.types = 'foobar'; + getRoleByName.mockResolvedValue({ + permissions: { + [PermissionTypes.PEOPLE_PICKER]: { + [Permissions.VIEW_USERS]: true, + [Permissions.VIEW_GROUPS]: false, + [Permissions.VIEW_ROLES]: false, + }, + }, + }); + + await checkPeoplePickerAccess(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + + it('should deny when types is empty string and user has no permissions', async () => { + req.query.types = ''; + getRoleByName.mockResolvedValue({ + permissions: { + [PermissionTypes.PEOPLE_PICKER]: { + [Permissions.VIEW_USERS]: false, + [Permissions.VIEW_GROUPS]: false, + [Permissions.VIEW_ROLES]: false, + }, + }, + }); + + await checkPeoplePickerAccess(req, res, next); + + expect(res.status).toHaveBeenCalledWith(403); + expect(res.json).toHaveBeenCalledWith({ + error: 'Forbidden', + message: 'Insufficient permissions to search for users, groups, or roles', + }); + expect(next).not.toHaveBeenCalled(); + }); + + it('should treat types=public as mixed search since PUBLIC is not a searchable principal type', async () => { + req.query.types = PrincipalType.PUBLIC; + getRoleByName.mockResolvedValue({ + permissions: { + [PermissionTypes.PEOPLE_PICKER]: { + [Permissions.VIEW_USERS]: true, + [Permissions.VIEW_GROUPS]: false, + [Permissions.VIEW_ROLES]: false, + }, + }, + }); + + await checkPeoplePickerAccess(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + it('should allow mixed search when user has at least one permission', async () => { // No type specified = mixed search req.query.type = undefined; @@ -222,7 +387,7 @@ describe('checkPeoplePickerAccess', () => { await checkPeoplePickerAccess(req, res, next); expect(logger.error).toHaveBeenCalledWith( - '[checkPeoplePickerAccess][user123] checkPeoplePickerAccess error for req.query.type = undefined', + '[checkPeoplePickerAccess][user123] error for type=undefined, types=undefined', error, ); expect(res.status).toHaveBeenCalledWith(500); diff --git a/api/server/middleware/checkSharePublicAccess.js b/api/server/middleware/checkSharePublicAccess.js index 0e95b9f6f8..c7b65a077e 100644 --- a/api/server/middleware/checkSharePublicAccess.js +++ b/api/server/middleware/checkSharePublicAccess.js @@ -1,6 +1,6 @@ const { logger } = require('@librechat/data-schemas'); const { ResourceType, PermissionTypes, Permissions } = require('librechat-data-provider'); -const { getRoleByName } = require('~/models/Role'); +const { getRoleByName } = require('~/models'); /** * Maps resource types to their corresponding permission types diff --git a/api/server/middleware/checkSharePublicAccess.spec.js b/api/server/middleware/checkSharePublicAccess.spec.js index c73e71693b..605de2049e 100644 --- a/api/server/middleware/checkSharePublicAccess.spec.js +++ b/api/server/middleware/checkSharePublicAccess.spec.js @@ -1,8 +1,8 @@ const { ResourceType, PermissionTypes, Permissions } = require('librechat-data-provider'); const { checkSharePublicAccess } = require('./checkSharePublicAccess'); -const { getRoleByName } = require('~/models/Role'); +const { getRoleByName } = require('~/models'); -jest.mock('~/models/Role'); +jest.mock('~/models'); describe('checkSharePublicAccess middleware', () => { let mockReq; diff --git a/api/server/middleware/denyRequest.js b/api/server/middleware/denyRequest.js index 20360519cf..86054d0a23 100644 --- a/api/server/middleware/denyRequest.js +++ b/api/server/middleware/denyRequest.js @@ -43,7 +43,11 @@ const denyRequest = async (req, res, errorMessage) => { if (shouldSaveMessage) { await saveMessage( - req, + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { ...userMessage, user: req.user.id }, { context: `api/server/middleware/denyRequest.js - ${responseText}` }, ); diff --git a/api/server/middleware/error.js b/api/server/middleware/error.js index fef7e60ef7..5fa3562c30 100644 --- a/api/server/middleware/error.js +++ b/api/server/middleware/error.js @@ -2,8 +2,7 @@ const crypto = require('crypto'); const { logger } = require('@librechat/data-schemas'); const { parseConvo } = require('librechat-data-provider'); const { sendEvent, handleError, sanitizeMessageForTransmit } = require('@librechat/api'); -const { saveMessage, getMessages } = require('~/models/Message'); -const { getConvo } = require('~/models/Conversation'); +const { saveMessage, getMessages, getConvo } = require('~/models'); /** * Processes an error with provided options, saves the error message and sends a corresponding SSE response @@ -49,7 +48,11 @@ const sendError = async (req, res, options, callback) => { if (shouldSaveMessage) { await saveMessage( - req, + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { ...errorMessage, user }, { context: 'api/server/utils/streamResponse.js - sendError', diff --git a/api/server/middleware/limiters/forkLimiters.js b/api/server/middleware/limiters/forkLimiters.js index e0aa65700c..f1e9b15f11 100644 --- a/api/server/middleware/limiters/forkLimiters.js +++ b/api/server/middleware/limiters/forkLimiters.js @@ -48,7 +48,7 @@ const createForkHandler = (ip = true) => { }; await logViolation(req, res, type, errorMessage, forkViolationScore); - res.status(429).json({ message: 'Too many conversation fork requests. Try again later' }); + res.status(429).json({ message: 'Too many requests. Try again later' }); }; }; diff --git a/api/server/middleware/roles/access.spec.js b/api/server/middleware/roles/access.spec.js index 9de840819d..16fb6df138 100644 --- a/api/server/middleware/roles/access.spec.js +++ b/api/server/middleware/roles/access.spec.js @@ -2,7 +2,7 @@ const mongoose = require('mongoose'); const { MongoMemoryServer } = require('mongodb-memory-server'); const { checkAccess, generateCheckAccess } = require('@librechat/api'); const { PermissionTypes, Permissions } = require('librechat-data-provider'); -const { getRoleByName } = require('~/models/Role'); +const { getRoleByName } = require('~/models'); const { Role } = require('~/db/models'); // Mock the logger from @librechat/data-schemas diff --git a/api/server/middleware/roles/capabilities.js b/api/server/middleware/roles/capabilities.js new file mode 100644 index 0000000000..6f2aa43e96 --- /dev/null +++ b/api/server/middleware/roles/capabilities.js @@ -0,0 +1,14 @@ +const { generateCapabilityCheck, capabilityContextMiddleware } = require('@librechat/api'); +const { getUserPrincipals, hasCapabilityForPrincipals } = require('~/models'); + +const { hasCapability, requireCapability, hasConfigCapability } = generateCapabilityCheck({ + getUserPrincipals, + hasCapabilityForPrincipals, +}); + +module.exports = { + hasCapability, + requireCapability, + hasConfigCapability, + capabilityContextMiddleware, +}; diff --git a/api/server/middleware/roles/index.js b/api/server/middleware/roles/index.js index f01b884e5a..e6c315d007 100644 --- a/api/server/middleware/roles/index.js +++ b/api/server/middleware/roles/index.js @@ -1,5 +1,15 @@ +const { + hasCapability, + requireCapability, + hasConfigCapability, + capabilityContextMiddleware, +} = require('./capabilities'); const checkAdmin = require('./admin'); module.exports = { checkAdmin, + hasCapability, + requireCapability, + hasConfigCapability, + capabilityContextMiddleware, }; diff --git a/api/server/middleware/validate/convoAccess.js b/api/server/middleware/validate/convoAccess.js index 127bfdc530..ef1eea8f37 100644 --- a/api/server/middleware/validate/convoAccess.js +++ b/api/server/middleware/validate/convoAccess.js @@ -1,8 +1,8 @@ const { isEnabled } = require('@librechat/api'); const { Constants, ViolationTypes, Time } = require('librechat-data-provider'); -const { searchConversation } = require('~/models/Conversation'); const denyRequest = require('~/server/middleware/denyRequest'); const { logViolation, getLogStores } = require('~/cache'); +const { searchConversation } = require('~/models'); const { USE_REDIS, CONVO_ACCESS_VIOLATION_SCORE: score = 0 } = process.env ?? {}; diff --git a/api/server/routes/__test-utils__/convos-route-mocks.js b/api/server/routes/__test-utils__/convos-route-mocks.js new file mode 100644 index 0000000000..0929e0759d --- /dev/null +++ b/api/server/routes/__test-utils__/convos-route-mocks.js @@ -0,0 +1,98 @@ +module.exports = { + agents: () => ({ sleep: jest.fn() }), + + api: (overrides = {}) => ({ + isEnabled: jest.fn(), + resolveImportMaxFileSize: jest.fn(() => 262144000), + createAxiosInstance: jest.fn(() => ({ + get: jest.fn(), + post: jest.fn(), + put: jest.fn(), + delete: jest.fn(), + })), + logAxiosError: jest.fn(), + ...overrides, + }), + + dataSchemas: () => ({ + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, + createModels: jest.fn(() => ({ + User: {}, + Conversation: {}, + Message: {}, + SharedLink: {}, + })), + }), + + dataProvider: (overrides = {}) => ({ + CacheKeys: { GEN_TITLE: 'GEN_TITLE' }, + EModelEndpoint: { + azureAssistants: 'azureAssistants', + assistants: 'assistants', + }, + ...overrides, + }), + + conversationModel: () => ({ + getConvosByCursor: jest.fn(), + getConvo: jest.fn(), + deleteConvos: jest.fn(), + saveConvo: jest.fn(), + }), + + toolCallModel: () => ({ deleteToolCalls: jest.fn() }), + + sharedModels: () => ({ + getConvosByCursor: jest.fn(), + getConvo: jest.fn(), + deleteConvos: jest.fn(), + saveConvo: jest.fn(), + deleteAllSharedLinks: jest.fn(), + deleteConvoSharedLink: jest.fn(), + deleteToolCalls: jest.fn(), + }), + + requireJwtAuth: () => (req, res, next) => next(), + + middlewarePassthrough: () => ({ + createImportLimiters: jest.fn(() => ({ + importIpLimiter: (req, res, next) => next(), + importUserLimiter: (req, res, next) => next(), + })), + createForkLimiters: jest.fn(() => ({ + forkIpLimiter: (req, res, next) => next(), + forkUserLimiter: (req, res, next) => next(), + })), + configMiddleware: (req, res, next) => next(), + validateConvoAccess: (req, res, next) => next(), + }), + + forkUtils: () => ({ + forkConversation: jest.fn(), + duplicateConversation: jest.fn(), + }), + + importUtils: () => ({ importConversations: jest.fn() }), + + logStores: () => jest.fn(), + + multerSetup: () => ({ + storage: {}, + importFileFilter: jest.fn(), + }), + + multerLib: () => + jest.fn(() => ({ + single: jest.fn(() => (req, res, next) => { + req.file = { path: '/tmp/test-file.json' }; + next(); + }), + })), + + assistantEndpoint: () => ({ initializeClient: jest.fn() }), +}; diff --git a/api/server/routes/__tests__/convos-duplicate-ratelimit.spec.js b/api/server/routes/__tests__/convos-duplicate-ratelimit.spec.js new file mode 100644 index 0000000000..a75c11ccba --- /dev/null +++ b/api/server/routes/__tests__/convos-duplicate-ratelimit.spec.js @@ -0,0 +1,137 @@ +const express = require('express'); +const request = require('supertest'); + +const MOCKS = '../__test-utils__/convos-route-mocks'; + +jest.mock('@librechat/agents', () => require(MOCKS).agents()); +jest.mock('@librechat/api', () => require(MOCKS).api({ limiterCache: jest.fn(() => undefined) })); +jest.mock('@librechat/data-schemas', () => require(MOCKS).dataSchemas()); +jest.mock('librechat-data-provider', () => + require(MOCKS).dataProvider({ ViolationTypes: { FILE_UPLOAD_LIMIT: 'file_upload_limit' } }), +); + +jest.mock('~/cache/logViolation', () => jest.fn().mockResolvedValue(undefined)); +jest.mock('~/cache/getLogStores', () => require(MOCKS).logStores()); +jest.mock('~/models', () => ({ + ...require(MOCKS).sharedModels(), + ...require(MOCKS).conversationModel(), + ...require(MOCKS).toolCallModel(), +})); +jest.mock('~/server/middleware/requireJwtAuth', () => require(MOCKS).requireJwtAuth()); + +jest.mock('~/server/middleware', () => { + const { createForkLimiters } = jest.requireActual('~/server/middleware/limiters/forkLimiters'); + return { + createImportLimiters: jest.fn(() => ({ + importIpLimiter: (req, res, next) => next(), + importUserLimiter: (req, res, next) => next(), + })), + createForkLimiters, + configMiddleware: (req, res, next) => next(), + validateConvoAccess: (req, res, next) => next(), + }; +}); + +jest.mock('~/server/utils/import/fork', () => require(MOCKS).forkUtils()); +jest.mock('~/server/utils/import', () => require(MOCKS).importUtils()); +jest.mock('~/server/routes/files/multer', () => require(MOCKS).multerSetup()); +jest.mock('multer', () => require(MOCKS).multerLib()); +jest.mock('~/server/services/Endpoints/azureAssistants', () => require(MOCKS).assistantEndpoint()); +jest.mock('~/server/services/Endpoints/assistants', () => require(MOCKS).assistantEndpoint()); + +describe('POST /api/convos/duplicate - Rate Limiting', () => { + let app; + let duplicateConversation; + const savedEnv = {}; + + beforeAll(() => { + savedEnv.FORK_USER_MAX = process.env.FORK_USER_MAX; + savedEnv.FORK_USER_WINDOW = process.env.FORK_USER_WINDOW; + savedEnv.FORK_IP_MAX = process.env.FORK_IP_MAX; + savedEnv.FORK_IP_WINDOW = process.env.FORK_IP_WINDOW; + }); + + afterAll(() => { + for (const key of Object.keys(savedEnv)) { + if (savedEnv[key] === undefined) { + delete process.env[key]; + } else { + process.env[key] = savedEnv[key]; + } + } + }); + + const setupApp = () => { + jest.clearAllMocks(); + jest.isolateModules(() => { + const convosRouter = require('../convos'); + ({ duplicateConversation } = require('~/server/utils/import/fork')); + + app = express(); + app.use(express.json()); + app.use((req, res, next) => { + req.user = { id: 'rate-limit-test-user' }; + next(); + }); + app.use('/api/convos', convosRouter); + }); + + duplicateConversation.mockResolvedValue({ + conversation: { conversationId: 'duplicated-conv' }, + }); + }; + + describe('user limit', () => { + beforeEach(() => { + process.env.FORK_USER_MAX = '2'; + process.env.FORK_USER_WINDOW = '1'; + process.env.FORK_IP_MAX = '100'; + process.env.FORK_IP_WINDOW = '1'; + setupApp(); + }); + + it('should return 429 after exceeding the user rate limit', async () => { + const userMax = parseInt(process.env.FORK_USER_MAX, 10); + + for (let i = 0; i < userMax; i++) { + const res = await request(app) + .post('/api/convos/duplicate') + .send({ conversationId: 'conv-123' }); + expect(res.status).toBe(201); + } + + const res = await request(app) + .post('/api/convos/duplicate') + .send({ conversationId: 'conv-123' }); + expect(res.status).toBe(429); + expect(res.body.message).toMatch(/too many/i); + }); + }); + + describe('IP limit', () => { + beforeEach(() => { + process.env.FORK_USER_MAX = '100'; + process.env.FORK_USER_WINDOW = '1'; + process.env.FORK_IP_MAX = '2'; + process.env.FORK_IP_WINDOW = '1'; + setupApp(); + }); + + it('should return 429 after exceeding the IP rate limit', async () => { + const ipMax = parseInt(process.env.FORK_IP_MAX, 10); + + for (let i = 0; i < ipMax; i++) { + const res = await request(app) + .post('/api/convos/duplicate') + .send({ conversationId: 'conv-123' }); + expect(res.status).toBe(201); + } + + const res = await request(app) + .post('/api/convos/duplicate') + .send({ conversationId: 'conv-123' }); + expect(res.status).toBe(429); + expect(res.body.message).toMatch(/too many/i); + }); + }); +}); diff --git a/api/server/routes/__tests__/convos-import.spec.js b/api/server/routes/__tests__/convos-import.spec.js new file mode 100644 index 0000000000..c4ea139931 --- /dev/null +++ b/api/server/routes/__tests__/convos-import.spec.js @@ -0,0 +1,98 @@ +const express = require('express'); +const request = require('supertest'); +const multer = require('multer'); + +const importFileFilter = (req, file, cb) => { + if (file.mimetype === 'application/json') { + cb(null, true); + } else { + cb(new Error('Only JSON files are allowed'), false); + } +}; + +/** Proxy app that mirrors the production multer + error-handling pattern */ +function createImportApp(fileSize) { + const app = express(); + const upload = multer({ + storage: multer.memoryStorage(), + fileFilter: importFileFilter, + limits: { fileSize }, + }); + const uploadSingle = upload.single('file'); + + function handleUpload(req, res, next) { + uploadSingle(req, res, (err) => { + if (err && err.code === 'LIMIT_FILE_SIZE') { + return res.status(413).json({ message: 'File exceeds the maximum allowed size' }); + } + if (err) { + return next(err); + } + next(); + }); + } + + app.post('/import', handleUpload, (req, res) => { + res.status(201).json({ message: 'success', size: req.file.size }); + }); + + app.use((err, _req, res, _next) => { + res.status(400).json({ error: err.message }); + }); + + return app; +} + +describe('Conversation Import - Multer File Size Limits', () => { + describe('multer rejects files exceeding the configured limit', () => { + it('returns 413 for files larger than the limit', async () => { + const limit = 1024; + const app = createImportApp(limit); + const oversized = Buffer.alloc(limit + 512, 'x'); + + const res = await request(app) + .post('/import') + .attach('file', oversized, { filename: 'import.json', contentType: 'application/json' }); + + expect(res.status).toBe(413); + expect(res.body.message).toBe('File exceeds the maximum allowed size'); + }); + + it('accepts files within the limit', async () => { + const limit = 4096; + const app = createImportApp(limit); + const valid = Buffer.from(JSON.stringify({ title: 'test' })); + + const res = await request(app) + .post('/import') + .attach('file', valid, { filename: 'import.json', contentType: 'application/json' }); + + expect(res.status).toBe(201); + expect(res.body.message).toBe('success'); + }); + + it('rejects at the exact boundary (limit + 1 byte)', async () => { + const limit = 512; + const app = createImportApp(limit); + const boundary = Buffer.alloc(limit + 1, 'a'); + + const res = await request(app) + .post('/import') + .attach('file', boundary, { filename: 'import.json', contentType: 'application/json' }); + + expect(res.status).toBe(413); + }); + + it('accepts a file just under the limit', async () => { + const limit = 512; + const app = createImportApp(limit); + const underLimit = Buffer.alloc(limit - 1, 'b'); + + const res = await request(app) + .post('/import') + .attach('file', underLimit, { filename: 'import.json', contentType: 'application/json' }); + + expect(res.status).toBe(201); + }); + }); +}); diff --git a/api/server/routes/__tests__/convos.spec.js b/api/server/routes/__tests__/convos.spec.js index 931ef006d0..23978f28e9 100644 --- a/api/server/routes/__tests__/convos.spec.js +++ b/api/server/routes/__tests__/convos.spec.js @@ -1,116 +1,33 @@ const express = require('express'); const request = require('supertest'); -jest.mock('@librechat/agents', () => ({ - sleep: jest.fn(), -})); +const MOCKS = '../__test-utils__/convos-route-mocks'; -jest.mock('@librechat/api', () => ({ - isEnabled: jest.fn(), - createAxiosInstance: jest.fn(() => ({ - get: jest.fn(), - post: jest.fn(), - put: jest.fn(), - delete: jest.fn(), - })), - logAxiosError: jest.fn(), -})); - -jest.mock('@librechat/data-schemas', () => ({ - logger: { - debug: jest.fn(), - info: jest.fn(), - warn: jest.fn(), - error: jest.fn(), - }, - createModels: jest.fn(() => ({ - User: {}, - Conversation: {}, - Message: {}, - SharedLink: {}, - })), -})); - -jest.mock('~/models/Conversation', () => ({ - getConvosByCursor: jest.fn(), - getConvo: jest.fn(), - deleteConvos: jest.fn(), - saveConvo: jest.fn(), -})); - -jest.mock('~/models/ToolCall', () => ({ - deleteToolCalls: jest.fn(), -})); - -jest.mock('~/models', () => ({ - deleteAllSharedLinks: jest.fn(), - deleteConvoSharedLink: jest.fn(), -})); - -jest.mock('~/server/middleware/requireJwtAuth', () => (req, res, next) => next()); - -jest.mock('~/server/middleware', () => ({ - createImportLimiters: jest.fn(() => ({ - importIpLimiter: (req, res, next) => next(), - importUserLimiter: (req, res, next) => next(), - })), - createForkLimiters: jest.fn(() => ({ - forkIpLimiter: (req, res, next) => next(), - forkUserLimiter: (req, res, next) => next(), - })), - configMiddleware: (req, res, next) => next(), - validateConvoAccess: (req, res, next) => next(), -})); - -jest.mock('~/server/utils/import/fork', () => ({ - forkConversation: jest.fn(), - duplicateConversation: jest.fn(), -})); - -jest.mock('~/server/utils/import', () => ({ - importConversations: jest.fn(), -})); - -jest.mock('~/cache/getLogStores', () => jest.fn()); - -jest.mock('~/server/routes/files/multer', () => ({ - storage: {}, - importFileFilter: jest.fn(), -})); - -jest.mock('multer', () => { - return jest.fn(() => ({ - single: jest.fn(() => (req, res, next) => { - req.file = { path: '/tmp/test-file.json' }; - next(); - }), - })); -}); - -jest.mock('librechat-data-provider', () => ({ - CacheKeys: { - GEN_TITLE: 'GEN_TITLE', - }, - EModelEndpoint: { - azureAssistants: 'azureAssistants', - assistants: 'assistants', - }, -})); - -jest.mock('~/server/services/Endpoints/azureAssistants', () => ({ - initializeClient: jest.fn(), -})); - -jest.mock('~/server/services/Endpoints/assistants', () => ({ - initializeClient: jest.fn(), -})); +jest.mock('@librechat/agents', () => require(MOCKS).agents()); +jest.mock('@librechat/api', () => require(MOCKS).api()); +jest.mock('@librechat/data-schemas', () => require(MOCKS).dataSchemas()); +jest.mock('librechat-data-provider', () => require(MOCKS).dataProvider()); +jest.mock('~/models', () => require(MOCKS).sharedModels()); +jest.mock('~/server/middleware/requireJwtAuth', () => require(MOCKS).requireJwtAuth()); +jest.mock('~/server/middleware', () => require(MOCKS).middlewarePassthrough()); +jest.mock('~/server/utils/import/fork', () => require(MOCKS).forkUtils()); +jest.mock('~/server/utils/import', () => require(MOCKS).importUtils()); +jest.mock('~/cache/getLogStores', () => require(MOCKS).logStores()); +jest.mock('~/server/routes/files/multer', () => require(MOCKS).multerSetup()); +jest.mock('multer', () => require(MOCKS).multerLib()); +jest.mock('~/server/services/Endpoints/azureAssistants', () => require(MOCKS).assistantEndpoint()); +jest.mock('~/server/services/Endpoints/assistants', () => require(MOCKS).assistantEndpoint()); describe('Convos Routes', () => { let app; let convosRouter; - const { deleteAllSharedLinks, deleteConvoSharedLink } = require('~/models'); - const { deleteConvos, saveConvo } = require('~/models/Conversation'); - const { deleteToolCalls } = require('~/models/ToolCall'); + const { + deleteAllSharedLinks, + deleteConvoSharedLink, + deleteToolCalls, + deleteConvos, + saveConvo, + } = require('~/models'); beforeAll(() => { convosRouter = require('../convos'); @@ -520,7 +437,7 @@ describe('Convos Routes', () => { expect(response.status).toBe(200); expect(response.body).toEqual(mockArchivedConvo); expect(saveConvo).toHaveBeenCalledWith( - expect.objectContaining({ user: { id: 'test-user-123' } }), + expect.objectContaining({ userId: 'test-user-123' }), { conversationId: mockConversationId, isArchived: true }, { context: `POST /api/convos/archive ${mockConversationId}` }, ); @@ -549,7 +466,7 @@ describe('Convos Routes', () => { expect(response.status).toBe(200); expect(response.body).toEqual(mockUnarchivedConvo); expect(saveConvo).toHaveBeenCalledWith( - expect.objectContaining({ user: { id: 'test-user-123' } }), + expect.objectContaining({ userId: 'test-user-123' }), { conversationId: mockConversationId, isArchived: false }, { context: `POST /api/convos/archive ${mockConversationId}` }, ); diff --git a/api/server/routes/__tests__/mcp.spec.js b/api/server/routes/__tests__/mcp.spec.js index e87fcf8f15..1ad8cac087 100644 --- a/api/server/routes/__tests__/mcp.spec.js +++ b/api/server/routes/__tests__/mcp.spec.js @@ -32,6 +32,9 @@ jest.mock('@librechat/api', () => { getFlowState: jest.fn(), completeOAuthFlow: jest.fn(), generateFlowId: jest.fn(), + resolveStateToFlowId: jest.fn(async (state) => state), + storeStateMapping: jest.fn(), + deleteStateMapping: jest.fn(), }, MCPTokenStorage: { storeTokens: jest.fn(), @@ -180,7 +183,10 @@ describe('MCP Routes', () => { MCPOAuthHandler.initiateOAuthFlow.mockResolvedValue({ authorizationUrl: 'https://oauth.example.com/auth', flowId: 'test-user-id:test-server', + flowMetadata: { state: 'random-state-value' }, }); + MCPOAuthHandler.storeStateMapping.mockResolvedValue(); + mockFlowManager.initFlow = jest.fn().mockResolvedValue(); const response = await request(app).get('/api/mcp/test-server/oauth/initiate').query({ userId: 'test-user-id', @@ -367,6 +373,121 @@ describe('MCP Routes', () => { expect(response.headers.location).toBe(`${basePath}/oauth/error?error=invalid_state`); }); + describe('CSRF fallback via active PENDING flow', () => { + it('should proceed when a fresh PENDING flow exists and no cookies are present', async () => { + const flowId = 'test-user-id:test-server'; + const mockFlowManager = { + getFlowState: jest.fn().mockResolvedValue({ + status: 'PENDING', + createdAt: Date.now(), + }), + completeFlow: jest.fn().mockResolvedValue(true), + deleteFlow: jest.fn().mockResolvedValue(true), + }; + const mockFlowState = { + serverName: 'test-server', + userId: 'test-user-id', + metadata: {}, + clientInfo: {}, + codeVerifier: 'test-verifier', + }; + + getLogStores.mockReturnValue({}); + require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager); + MCPOAuthHandler.getFlowState.mockResolvedValue(mockFlowState); + MCPOAuthHandler.completeOAuthFlow.mockResolvedValue({ + access_token: 'test-token', + }); + MCPTokenStorage.storeTokens.mockResolvedValue(); + mockRegistryInstance.getServerConfig.mockResolvedValue({}); + + const mockMcpManager = { + getUserConnection: jest.fn().mockResolvedValue({ + fetchTools: jest.fn().mockResolvedValue([]), + }), + }; + require('~/config').getMCPManager.mockReturnValue(mockMcpManager); + require('~/config').getOAuthReconnectionManager.mockReturnValue({ + clearReconnection: jest.fn(), + }); + require('~/server/services/Config/mcp').updateMCPServerTools.mockResolvedValue(); + + const response = await request(app) + .get('/api/mcp/test-server/oauth/callback') + .query({ code: 'test-code', state: flowId }); + + const basePath = getBasePath(); + expect(response.status).toBe(302); + expect(response.headers.location).toContain(`${basePath}/oauth/success`); + }); + + it('should reject when no PENDING flow exists and no cookies are present', async () => { + const flowId = 'test-user-id:test-server'; + const mockFlowManager = { + getFlowState: jest.fn().mockResolvedValue(null), + }; + + getLogStores.mockReturnValue({}); + require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager); + + const response = await request(app) + .get('/api/mcp/test-server/oauth/callback') + .query({ code: 'test-code', state: flowId }); + + const basePath = getBasePath(); + expect(response.status).toBe(302); + expect(response.headers.location).toBe( + `${basePath}/oauth/error?error=csrf_validation_failed`, + ); + }); + + it('should reject when only a COMPLETED flow exists (not PENDING)', async () => { + const flowId = 'test-user-id:test-server'; + const mockFlowManager = { + getFlowState: jest.fn().mockResolvedValue({ + status: 'COMPLETED', + createdAt: Date.now(), + }), + }; + + getLogStores.mockReturnValue({}); + require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager); + + const response = await request(app) + .get('/api/mcp/test-server/oauth/callback') + .query({ code: 'test-code', state: flowId }); + + const basePath = getBasePath(); + expect(response.status).toBe(302); + expect(response.headers.location).toBe( + `${basePath}/oauth/error?error=csrf_validation_failed`, + ); + }); + + it('should reject when PENDING flow is stale (older than PENDING_STALE_MS)', async () => { + const flowId = 'test-user-id:test-server'; + const mockFlowManager = { + getFlowState: jest.fn().mockResolvedValue({ + status: 'PENDING', + createdAt: Date.now() - 3 * 60 * 1000, + }), + }; + + getLogStores.mockReturnValue({}); + require('~/config').getFlowStateManager.mockReturnValue(mockFlowManager); + + const response = await request(app) + .get('/api/mcp/test-server/oauth/callback') + .query({ code: 'test-code', state: flowId }); + + const basePath = getBasePath(); + expect(response.status).toBe(302); + expect(response.headers.location).toBe( + `${basePath}/oauth/error?error=csrf_validation_failed`, + ); + }); + }); + it('should handle OAuth callback successfully', async () => { // mockRegistryInstance is defined at the top of the file const mockFlowManager = { @@ -1572,12 +1693,14 @@ describe('MCP Routes', () => { it('should return all server configs for authenticated user', async () => { const mockServerConfigs = { 'server-1': { - endpoint: 'http://server1.com', - name: 'Server 1', + type: 'sse', + url: 'http://server1.com/sse', + title: 'Server 1', }, 'server-2': { - endpoint: 'http://server2.com', - name: 'Server 2', + type: 'sse', + url: 'http://server2.com/sse', + title: 'Server 2', }, }; @@ -1586,7 +1709,18 @@ describe('MCP Routes', () => { const response = await request(app).get('/api/mcp/servers'); expect(response.status).toBe(200); - expect(response.body).toEqual(mockServerConfigs); + expect(response.body['server-1']).toMatchObject({ + type: 'sse', + url: 'http://server1.com/sse', + title: 'Server 1', + }); + expect(response.body['server-2']).toMatchObject({ + type: 'sse', + url: 'http://server2.com/sse', + title: 'Server 2', + }); + expect(response.body['server-1'].headers).toBeUndefined(); + expect(response.body['server-2'].headers).toBeUndefined(); expect(mockRegistryInstance.getAllServerConfigs).toHaveBeenCalledWith('test-user-id'); }); @@ -1641,10 +1775,10 @@ describe('MCP Routes', () => { const response = await request(app).post('/api/mcp/servers').send({ config: validConfig }); expect(response.status).toBe(201); - expect(response.body).toEqual({ - serverName: 'test-sse-server', - ...validConfig, - }); + expect(response.body.serverName).toBe('test-sse-server'); + expect(response.body.type).toBe('sse'); + expect(response.body.url).toBe('https://mcp-server.example.com/sse'); + expect(response.body.title).toBe('Test SSE Server'); expect(mockRegistryInstance.addServer).toHaveBeenCalledWith( 'temp_server_name', expect.objectContaining({ @@ -1698,6 +1832,78 @@ describe('MCP Routes', () => { expect(response.body.message).toBe('Invalid configuration'); }); + it('should reject SSE URL containing env variable references', async () => { + const response = await request(app) + .post('/api/mcp/servers') + .send({ + config: { + type: 'sse', + url: 'http://attacker.com/?secret=${JWT_SECRET}', + }, + }); + + expect(response.status).toBe(400); + expect(response.body.message).toBe('Invalid configuration'); + expect(mockRegistryInstance.addServer).not.toHaveBeenCalled(); + }); + + it('should reject streamable-http URL containing env variable references', async () => { + const response = await request(app) + .post('/api/mcp/servers') + .send({ + config: { + type: 'streamable-http', + url: 'http://attacker.com/?key=${CREDS_KEY}&iv=${CREDS_IV}', + }, + }); + + expect(response.status).toBe(400); + expect(response.body.message).toBe('Invalid configuration'); + expect(mockRegistryInstance.addServer).not.toHaveBeenCalled(); + }); + + it('should reject websocket URL containing env variable references', async () => { + const response = await request(app) + .post('/api/mcp/servers') + .send({ + config: { + type: 'websocket', + url: 'ws://attacker.com/?secret=${MONGO_URI}', + }, + }); + + expect(response.status).toBe(400); + expect(response.body.message).toBe('Invalid configuration'); + expect(mockRegistryInstance.addServer).not.toHaveBeenCalled(); + }); + + it('should redact secrets from create response', async () => { + const validConfig = { + type: 'sse', + url: 'https://mcp-server.example.com/sse', + title: 'Test Server', + }; + + mockRegistryInstance.addServer.mockResolvedValue({ + serverName: 'test-server', + config: { + ...validConfig, + apiKey: { source: 'admin', authorization_type: 'bearer', key: 'admin-secret-key' }, + oauth: { client_id: 'cid', client_secret: 'admin-oauth-secret' }, + headers: { Authorization: 'Bearer leaked-token' }, + }, + }); + + const response = await request(app).post('/api/mcp/servers').send({ config: validConfig }); + + expect(response.status).toBe(201); + expect(response.body.apiKey?.key).toBeUndefined(); + expect(response.body.oauth?.client_secret).toBeUndefined(); + expect(response.body.headers).toBeUndefined(); + expect(response.body.apiKey?.source).toBe('admin'); + expect(response.body.oauth?.client_id).toBe('cid'); + }); + it('should return 500 when registry throws error', async () => { const validConfig = { type: 'sse', @@ -1727,7 +1933,9 @@ describe('MCP Routes', () => { const response = await request(app).get('/api/mcp/servers/test-server'); expect(response.status).toBe(200); - expect(response.body).toEqual(mockConfig); + expect(response.body.type).toBe('sse'); + expect(response.body.url).toBe('https://mcp-server.example.com/sse'); + expect(response.body.title).toBe('Test Server'); expect(mockRegistryInstance.getServerConfig).toHaveBeenCalledWith( 'test-server', 'test-user-id', @@ -1743,6 +1951,29 @@ describe('MCP Routes', () => { expect(response.body).toEqual({ message: 'MCP server not found' }); }); + it('should redact secrets from get response', async () => { + mockRegistryInstance.getServerConfig.mockResolvedValue({ + type: 'sse', + url: 'https://mcp-server.example.com/sse', + title: 'Secret Server', + apiKey: { source: 'admin', authorization_type: 'bearer', key: 'decrypted-admin-key' }, + oauth: { client_id: 'cid', client_secret: 'decrypted-oauth-secret' }, + headers: { Authorization: 'Bearer internal-token' }, + oauth_headers: { 'X-OAuth': 'secret-value' }, + }); + + const response = await request(app).get('/api/mcp/servers/secret-server'); + + expect(response.status).toBe(200); + expect(response.body.title).toBe('Secret Server'); + expect(response.body.apiKey?.key).toBeUndefined(); + expect(response.body.apiKey?.source).toBe('admin'); + expect(response.body.oauth?.client_secret).toBeUndefined(); + expect(response.body.oauth?.client_id).toBe('cid'); + expect(response.body.headers).toBeUndefined(); + expect(response.body.oauth_headers).toBeUndefined(); + }); + it('should return 500 when registry throws error', async () => { mockRegistryInstance.getServerConfig.mockRejectedValue(new Error('Database error')); @@ -1769,7 +2000,9 @@ describe('MCP Routes', () => { .send({ config: updatedConfig }); expect(response.status).toBe(200); - expect(response.body).toEqual(updatedConfig); + expect(response.body.type).toBe('sse'); + expect(response.body.url).toBe('https://updated-mcp-server.example.com/sse'); + expect(response.body.title).toBe('Updated Server'); expect(mockRegistryInstance.updateServer).toHaveBeenCalledWith( 'test-server', expect.objectContaining({ @@ -1781,6 +2014,35 @@ describe('MCP Routes', () => { ); }); + it('should redact secrets from update response', async () => { + const validConfig = { + type: 'sse', + url: 'https://mcp-server.example.com/sse', + title: 'Updated Server', + }; + + mockRegistryInstance.updateServer.mockResolvedValue({ + ...validConfig, + apiKey: { source: 'admin', authorization_type: 'bearer', key: 'preserved-admin-key' }, + oauth: { client_id: 'cid', client_secret: 'preserved-oauth-secret' }, + headers: { Authorization: 'Bearer internal-token' }, + env: { DATABASE_URL: 'postgres://admin:pass@localhost/db' }, + }); + + const response = await request(app) + .patch('/api/mcp/servers/test-server') + .send({ config: validConfig }); + + expect(response.status).toBe(200); + expect(response.body.title).toBe('Updated Server'); + expect(response.body.apiKey?.key).toBeUndefined(); + expect(response.body.apiKey?.source).toBe('admin'); + expect(response.body.oauth?.client_secret).toBeUndefined(); + expect(response.body.oauth?.client_id).toBe('cid'); + expect(response.body.headers).toBeUndefined(); + expect(response.body.env).toBeUndefined(); + }); + it('should return 400 for invalid configuration', async () => { const invalidConfig = { type: 'sse', @@ -1797,6 +2059,51 @@ describe('MCP Routes', () => { expect(response.body.errors).toBeDefined(); }); + it('should reject SSE URL containing env variable references', async () => { + const response = await request(app) + .patch('/api/mcp/servers/test-server') + .send({ + config: { + type: 'sse', + url: 'http://attacker.com/?secret=${JWT_SECRET}', + }, + }); + + expect(response.status).toBe(400); + expect(response.body.message).toBe('Invalid configuration'); + expect(mockRegistryInstance.updateServer).not.toHaveBeenCalled(); + }); + + it('should reject streamable-http URL containing env variable references', async () => { + const response = await request(app) + .patch('/api/mcp/servers/test-server') + .send({ + config: { + type: 'streamable-http', + url: 'http://attacker.com/?key=${CREDS_KEY}', + }, + }); + + expect(response.status).toBe(400); + expect(response.body.message).toBe('Invalid configuration'); + expect(mockRegistryInstance.updateServer).not.toHaveBeenCalled(); + }); + + it('should reject websocket URL containing env variable references', async () => { + const response = await request(app) + .patch('/api/mcp/servers/test-server') + .send({ + config: { + type: 'websocket', + url: 'ws://attacker.com/?secret=${MONGO_URI}', + }, + }); + + expect(response.status).toBe(400); + expect(response.body.message).toBe('Invalid configuration'); + expect(mockRegistryInstance.updateServer).not.toHaveBeenCalled(); + }); + it('should return 500 when registry throws error', async () => { const validConfig = { type: 'sse', diff --git a/api/server/routes/__tests__/messages-delete.spec.js b/api/server/routes/__tests__/messages-delete.spec.js new file mode 100644 index 0000000000..714d497719 --- /dev/null +++ b/api/server/routes/__tests__/messages-delete.spec.js @@ -0,0 +1,199 @@ +const mongoose = require('mongoose'); +const express = require('express'); +const request = require('supertest'); +const { v4: uuidv4 } = require('uuid'); +const { MongoMemoryServer } = require('mongodb-memory-server'); + +jest.mock('@librechat/agents', () => ({ + sleep: jest.fn(), +})); + +jest.mock('@librechat/api', () => ({ + unescapeLaTeX: jest.fn((x) => x), + countTokens: jest.fn().mockResolvedValue(10), +})); + +jest.mock('@librechat/data-schemas', () => ({ + ...jest.requireActual('@librechat/data-schemas'), + logger: { + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + }, +})); + +jest.mock('librechat-data-provider', () => ({ + ...jest.requireActual('librechat-data-provider'), +})); + +jest.mock('~/models', () => ({ + saveConvo: jest.fn(), + getMessage: jest.fn(), + saveMessage: jest.fn(), + getMessages: jest.fn(), + updateMessage: jest.fn(), + deleteMessages: jest.fn(), + getConvosQueried: jest.fn(), + searchMessages: jest.fn(), + getMessagesByCursor: jest.fn(), +})); + +jest.mock('~/server/services/Artifacts/update', () => ({ + findAllArtifacts: jest.fn(), + replaceArtifactContent: jest.fn(), +})); + +jest.mock('~/server/middleware/requireJwtAuth', () => (req, res, next) => next()); + +jest.mock('~/server/middleware', () => ({ + requireJwtAuth: (req, res, next) => next(), + validateMessageReq: (req, res, next) => next(), +})); + +jest.mock('~/db/models', () => ({ + Message: { + findOne: jest.fn(), + find: jest.fn(), + meiliSearch: jest.fn(), + }, +})); + +/* ─── Model-level tests: real MongoDB, proves cross-user deletion is prevented ─── */ + +const { messageSchema } = require('@librechat/data-schemas'); + +describe('deleteMessages – model-level IDOR prevention', () => { + let mongoServer; + let Message; + + const ownerUserId = 'user-owner-111'; + const attackerUserId = 'user-attacker-222'; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + Message = mongoose.models.Message || mongoose.model('Message', messageSchema); + await mongoose.connect(mongoServer.getUri()); + }); + + afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); + }); + + beforeEach(async () => { + await Message.deleteMany({}); + }); + + it("should NOT delete another user's message when attacker supplies victim messageId", async () => { + const conversationId = uuidv4(); + const victimMsgId = 'victim-msg-001'; + + await Message.create({ + messageId: victimMsgId, + conversationId, + user: ownerUserId, + text: 'Sensitive owner data', + }); + + await Message.deleteMany({ messageId: victimMsgId, user: attackerUserId }); + + const victimMsg = await Message.findOne({ messageId: victimMsgId }).lean(); + expect(victimMsg).not.toBeNull(); + expect(victimMsg.user).toBe(ownerUserId); + expect(victimMsg.text).toBe('Sensitive owner data'); + }); + + it("should delete the user's own message", async () => { + const conversationId = uuidv4(); + const ownMsgId = 'own-msg-001'; + + await Message.create({ + messageId: ownMsgId, + conversationId, + user: ownerUserId, + text: 'My message', + }); + + const result = await Message.deleteMany({ messageId: ownMsgId, user: ownerUserId }); + expect(result.deletedCount).toBe(1); + + const deleted = await Message.findOne({ messageId: ownMsgId }).lean(); + expect(deleted).toBeNull(); + }); + + it('should scope deletion by conversationId, messageId, and user together', async () => { + const convoA = uuidv4(); + const convoB = uuidv4(); + + await Message.create([ + { messageId: 'msg-a1', conversationId: convoA, user: ownerUserId, text: 'A1' }, + { messageId: 'msg-b1', conversationId: convoB, user: ownerUserId, text: 'B1' }, + ]); + + await Message.deleteMany({ messageId: 'msg-a1', conversationId: convoA, user: attackerUserId }); + + const remaining = await Message.find({ user: ownerUserId }).lean(); + expect(remaining).toHaveLength(2); + }); +}); + +/* ─── Route-level tests: supertest + mocked deleteMessages ─── */ + +describe('DELETE /:conversationId/:messageId – route handler', () => { + let app; + const { deleteMessages } = require('~/models'); + + const authenticatedUserId = 'user-owner-123'; + + beforeAll(() => { + const messagesRouter = require('../messages'); + + app = express(); + app.use(express.json()); + app.use((req, res, next) => { + req.user = { id: authenticatedUserId }; + next(); + }); + app.use('/api/messages', messagesRouter); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should pass user and conversationId in the deleteMessages filter', async () => { + deleteMessages.mockResolvedValue({ deletedCount: 1 }); + + await request(app).delete('/api/messages/convo-1/msg-1'); + + expect(deleteMessages).toHaveBeenCalledTimes(1); + expect(deleteMessages).toHaveBeenCalledWith({ + messageId: 'msg-1', + conversationId: 'convo-1', + user: authenticatedUserId, + }); + }); + + it('should return 204 on successful deletion', async () => { + deleteMessages.mockResolvedValue({ deletedCount: 1 }); + + const response = await request(app).delete('/api/messages/convo-1/msg-owned'); + + expect(response.status).toBe(204); + expect(deleteMessages).toHaveBeenCalledWith({ + messageId: 'msg-owned', + conversationId: 'convo-1', + user: authenticatedUserId, + }); + }); + + it('should return 500 when deleteMessages throws', async () => { + deleteMessages.mockRejectedValue(new Error('DB failure')); + + const response = await request(app).delete('/api/messages/convo-1/msg-1'); + + expect(response.status).toBe(500); + expect(response.body).toEqual({ error: 'Internal server error' }); + }); +}); diff --git a/api/server/routes/accessPermissions.test.js b/api/server/routes/accessPermissions.test.js index 81c21c8667..ddbe702f15 100644 --- a/api/server/routes/accessPermissions.test.js +++ b/api/server/routes/accessPermissions.test.js @@ -5,7 +5,7 @@ const { v4: uuidv4 } = require('uuid'); const { createMethods } = require('@librechat/data-schemas'); const { MongoMemoryServer } = require('mongodb-memory-server'); const { ResourceType, PermissionBits } = require('librechat-data-provider'); -const { createAgent } = require('~/models/Agent'); +const { createAgent } = require('~/models'); /** * Mock the PermissionsController to isolate route testing diff --git a/api/server/routes/admin/auth.js b/api/server/routes/admin/auth.js index 291b5eaaf8..e19adf54a9 100644 --- a/api/server/routes/admin/auth.js +++ b/api/server/routes/admin/auth.js @@ -3,23 +3,23 @@ const passport = require('passport'); const { randomState } = require('openid-client'); const { logger } = require('@librechat/data-schemas'); const { CacheKeys } = require('librechat-data-provider'); -const { - requireAdmin, - getAdminPanelUrl, - exchangeAdminCode, - createSetBalanceConfig, -} = require('@librechat/api'); +const { SystemCapabilities } = require('@librechat/data-schemas'); +const { getAdminPanelUrl, exchangeAdminCode, createSetBalanceConfig } = require('@librechat/api'); const { loginController } = require('~/server/controllers/auth/LoginController'); const { createOAuthHandler } = require('~/server/controllers/auth/oauth'); +const { findBalanceByUser, upsertBalanceFields } = require('~/models'); const { getAppConfig } = require('~/server/services/Config'); +const { requireCapability } = require('~/server/middleware'); const getLogStores = require('~/cache/getLogStores'); const { getOpenIdConfig } = require('~/strategies'); const middleware = require('~/server/middleware'); -const { Balance } = require('~/db/models'); + +const requireAdminAccess = requireCapability(SystemCapabilities.ACCESS_ADMIN); const setBalanceConfig = createSetBalanceConfig({ getAppConfig, - Balance, + findBalanceByUser, + upsertBalanceFields, }); const router = express.Router(); @@ -30,12 +30,12 @@ router.post( middleware.loginLimiter, middleware.checkBan, middleware.requireLocalAuth, - requireAdmin, + requireAdminAccess, setBalanceConfig, loginController, ); -router.get('/verify', middleware.requireJwtAuth, requireAdmin, (req, res) => { +router.get('/verify', middleware.requireJwtAuth, requireAdminAccess, (req, res) => { const { password: _p, totpSecret: _t, __v, ...user } = req.user; user.id = user._id.toString(); res.status(200).json({ user }); @@ -66,7 +66,7 @@ router.get( failureMessage: true, session: false, }), - requireAdmin, + requireAdminAccess, setBalanceConfig, middleware.checkDomainAllowed, createOAuthHandler(`${getAdminPanelUrl()}/auth/openid/callback`), diff --git a/api/server/routes/agents/actions.js b/api/server/routes/agents/actions.js index 12168ba28a..b3b34f3f1c 100644 --- a/api/server/routes/agents/actions.js +++ b/api/server/routes/agents/actions.js @@ -12,19 +12,21 @@ const { validateActionDomain, validateAndParseOpenAPISpec, } = require('librechat-data-provider'); -const { encryptMetadata, domainParser } = require('~/server/services/ActionService'); +const { + legacyDomainEncode, + encryptMetadata, + domainParser, +} = require('~/server/services/ActionService'); const { findAccessibleResources } = require('~/server/services/PermissionService'); -const { getAgent, updateAgent, getListAgentsByAccess } = require('~/models/Agent'); -const { updateAction, getActions, deleteAction } = require('~/models/Action'); +const db = require('~/models'); const { canAccessAgentResource } = require('~/server/middleware'); -const { getRoleByName } = require('~/models/Role'); const router = express.Router(); const checkAgentCreate = generateCheckAccess({ permissionType: PermissionTypes.AGENTS, permissions: [Permissions.USE, Permissions.CREATE], - getRoleByName, + getRoleByName: db.getRoleByName, }); /** @@ -43,13 +45,15 @@ router.get('/', async (req, res) => { requiredPermissions: PermissionBits.EDIT, }); - const agentsResponse = await getListAgentsByAccess({ + const agentsResponse = await db.getListAgentsByAccess({ accessibleIds: editableAgentObjectIds, }); const editableAgentIds = agentsResponse.data.map((agent) => agent.id); const actions = - editableAgentIds.length > 0 ? await getActions({ agent_id: { $in: editableAgentIds } }) : []; + editableAgentIds.length > 0 + ? await db.getActions({ agent_id: { $in: editableAgentIds } }) + : []; res.json(actions); } catch (error) { @@ -119,20 +123,21 @@ router.post( return res.status(400).json({ message: 'Domain not allowed' }); } - let { domain } = metadata; - domain = await domainParser(domain, true); + const encodedDomain = await domainParser(metadata.domain, true); - if (!domain) { + if (!encodedDomain) { return res.status(400).json({ message: 'No domain provided' }); } + const legacyDomain = legacyDomainEncode(metadata.domain); + const action_id = _action_id ?? nanoid(); const initialPromises = []; // Permissions already validated by middleware - load agent directly - initialPromises.push(getAgent({ id: agent_id })); + initialPromises.push(db.getAgent({ id: agent_id })); if (_action_id) { - initialPromises.push(getActions({ action_id }, true)); + initialPromises.push(db.getActions({ action_id }, true)); } /** @type {[Agent, [Action|undefined]]} */ @@ -143,6 +148,9 @@ router.post( if (actions_result && actions_result.length) { const action = actions_result[0]; + if (action.agent_id !== agent_id) { + return res.status(403).json({ message: 'Action does not belong to this agent' }); + } metadata = { ...action.metadata, ...metadata }; } @@ -157,17 +165,26 @@ router.post( actions.push(action); } - actions.push(`${domain}${actionDelimiter}${action_id}`); + actions.push(`${encodedDomain}${actionDelimiter}${action_id}`); /** @type {string[]}} */ const { tools: _tools = [] } = agent; + const shouldRemoveAgentTool = (tool) => { + if (!tool) { + return false; + } + return ( + tool.includes(encodedDomain) || tool.includes(legacyDomain) || tool.includes(action_id) + ); + }; + const tools = _tools - .filter((tool) => !(tool && (tool.includes(domain) || tool.includes(action_id)))) - .concat(functions.map((tool) => `${tool.function.name}${actionDelimiter}${domain}`)); + .filter((tool) => !shouldRemoveAgentTool(tool)) + .concat(functions.map((tool) => `${tool.function.name}${actionDelimiter}${encodedDomain}`)); // Force version update since actions are changing - const updatedAgent = await updateAgent( + const updatedAgent = await db.updateAgent( { id: agent_id }, { tools, actions }, { @@ -184,7 +201,7 @@ router.post( } /** @type {[Action]} */ - const updatedAction = await updateAction({ action_id }, actionUpdateData); + const updatedAction = await db.updateAction({ action_id, agent_id }, actionUpdateData); const sensitiveFields = ['api_key', 'oauth_client_id', 'oauth_client_secret']; for (let field of sensitiveFields) { @@ -221,37 +238,43 @@ router.delete( const { agent_id, action_id } = req.params; // Permissions already validated by middleware - load agent directly - const agent = await getAgent({ id: agent_id }); + const agent = await db.getAgent({ id: agent_id }); if (!agent) { return res.status(404).json({ message: 'Agent not found for deleting action' }); } const { tools = [], actions = [] } = agent; - let domain = ''; + let storedDomain = ''; const updatedActions = actions.filter((action) => { if (action.includes(action_id)) { - [domain] = action.split(actionDelimiter); + [storedDomain] = action.split(actionDelimiter); return false; } return true; }); - domain = await domainParser(domain, true); - - if (!domain) { + if (!storedDomain) { return res.status(400).json({ message: 'No domain provided' }); } - const updatedTools = tools.filter((tool) => !(tool && tool.includes(domain))); + const updatedTools = tools.filter( + (tool) => !(tool && (tool.includes(storedDomain) || tool.includes(action_id))), + ); // Force version update since actions are being removed - await updateAgent( + await db.updateAgent( { id: agent_id }, { tools: updatedTools, actions: updatedActions }, { updatingUserId: req.user.id, forceVersion: true }, ); - await deleteAction({ action_id }); + const deleted = await db.deleteAction({ action_id, agent_id }); + if (!deleted) { + logger.warn('[Agent Action Delete] No matching action document found', { + action_id, + agent_id, + }); + } res.status(200).json({ message: 'Action deleted successfully' }); } catch (error) { const message = 'Trouble deleting the Agent Action'; diff --git a/api/server/routes/agents/chat.js b/api/server/routes/agents/chat.js index 37b83f4f54..0543b0b1aa 100644 --- a/api/server/routes/agents/chat.js +++ b/api/server/routes/agents/chat.js @@ -11,7 +11,7 @@ const { const { initializeClient } = require('~/server/services/Endpoints/agents'); const AgentController = require('~/server/controllers/agents/request'); const addTitle = require('~/server/services/Endpoints/agents/title'); -const { getRoleByName } = require('~/models/Role'); +const { getRoleByName } = require('~/models'); const router = express.Router(); diff --git a/api/server/routes/agents/index.js b/api/server/routes/agents/index.js index f8d39cb4d8..86966a3f3e 100644 --- a/api/server/routes/agents/index.js +++ b/api/server/routes/agents/index.js @@ -10,8 +10,8 @@ const { messageUserLimiter, } = require('~/server/middleware'); const { saveMessage } = require('~/models'); -const openai = require('./openai'); const responses = require('./responses'); +const openai = require('./openai'); const { v1 } = require('./v1'); const chat = require('./chat'); @@ -76,52 +76,62 @@ router.get('/chat/stream/:streamId', async (req, res) => { logger.debug(`[AgentStream] Client subscribed to ${streamId}, resume: ${isResume}`); - // Send sync event with resume state for ALL reconnecting clients - // This supports multi-tab scenarios where each tab needs run step data - if (isResume) { - const resumeState = await GenerationJobManager.getResumeState(streamId); - if (resumeState && !res.writableEnded) { - // Send sync event with run steps AND aggregatedContent - // Client will use aggregatedContent to initialize message state - res.write(`event: message\ndata: ${JSON.stringify({ sync: true, resumeState })}\n\n`); + const writeEvent = (event) => { + if (!res.writableEnded) { + res.write(`event: message\ndata: ${JSON.stringify(event)}\n\n`); if (typeof res.flush === 'function') { res.flush(); } - logger.debug( - `[AgentStream] Sent sync event for ${streamId} with ${resumeState.runSteps.length} run steps`, - ); } - } + }; - const result = await GenerationJobManager.subscribe( - streamId, - (event) => { - if (!res.writableEnded) { - res.write(`event: message\ndata: ${JSON.stringify(event)}\n\n`); + const onDone = (event) => { + writeEvent(event); + res.end(); + }; + + const onError = (error) => { + if (!res.writableEnded) { + res.write(`event: error\ndata: ${JSON.stringify({ error })}\n\n`); + if (typeof res.flush === 'function') { + res.flush(); + } + res.end(); + } + }; + + let result; + + if (isResume) { + const { subscription, resumeState, pendingEvents } = + await GenerationJobManager.subscribeWithResume(streamId, writeEvent, onDone, onError); + + if (!res.writableEnded) { + if (resumeState) { + res.write( + `event: message\ndata: ${JSON.stringify({ sync: true, resumeState, pendingEvents })}\n\n`, + ); if (typeof res.flush === 'function') { res.flush(); } - } - }, - (event) => { - if (!res.writableEnded) { - res.write(`event: message\ndata: ${JSON.stringify(event)}\n\n`); - if (typeof res.flush === 'function') { - res.flush(); + GenerationJobManager.markSyncSent(streamId); + logger.debug( + `[AgentStream] Sent sync event for ${streamId} with ${resumeState.runSteps.length} run steps, ${pendingEvents.length} pending events`, + ); + } else if (pendingEvents.length > 0) { + for (const event of pendingEvents) { + writeEvent(event); } - res.end(); + logger.warn( + `[AgentStream] Resume state null for ${streamId}, replayed ${pendingEvents.length} gap events directly`, + ); } - }, - (error) => { - if (!res.writableEnded) { - res.write(`event: error\ndata: ${JSON.stringify({ error })}\n\n`); - if (typeof res.flush === 'function') { - res.flush(); - } - res.end(); - } - }, - ); + } + + result = subscription; + } else { + result = await GenerationJobManager.subscribe(streamId, writeEvent, onDone, onError); + } if (!result) { return res.status(404).json({ error: 'Failed to subscribe to stream' }); @@ -253,9 +263,15 @@ router.post('/chat/abort', async (req, res) => { }; try { - await saveMessage(req, responseMessage, { - context: 'api/server/routes/agents/index.js - abort endpoint', - }); + await saveMessage( + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, + responseMessage, + { context: 'api/server/routes/agents/index.js - abort endpoint' }, + ); logger.debug(`[AgentStream] Saved partial response for: ${jobStreamId}`); } catch (saveError) { logger.error(`[AgentStream] Failed to save partial response: ${saveError.message}`); diff --git a/api/server/routes/agents/openai.js b/api/server/routes/agents/openai.js index 9a0d9a3564..72e3da6c5a 100644 --- a/api/server/routes/agents/openai.js +++ b/api/server/routes/agents/openai.js @@ -29,26 +29,24 @@ const { GetModelController, } = require('~/server/controllers/agents/openai'); const { getEffectivePermissions } = require('~/server/services/PermissionService'); -const { validateAgentApiKey, findUser } = require('~/models'); const { configMiddleware } = require('~/server/middleware'); -const { getRoleByName } = require('~/models/Role'); -const { getAgent } = require('~/models/Agent'); +const db = require('~/models'); const router = express.Router(); const requireApiKeyAuth = createRequireApiKeyAuth({ - validateAgentApiKey, - findUser, + validateAgentApiKey: db.validateAgentApiKey, + findUser: db.findUser, }); const checkRemoteAgentsFeature = generateCheckAccess({ permissionType: PermissionTypes.REMOTE_AGENTS, permissions: [Permissions.USE], - getRoleByName, + getRoleByName: db.getRoleByName, }); const checkAgentPermission = createCheckRemoteAgentAccess({ - getAgent, + getAgent: db.getAgent, getEffectivePermissions, }); diff --git a/api/server/routes/agents/responses.js b/api/server/routes/agents/responses.js index 431942e921..2c118e0597 100644 --- a/api/server/routes/agents/responses.js +++ b/api/server/routes/agents/responses.js @@ -32,26 +32,24 @@ const { listModels, } = require('~/server/controllers/agents/responses'); const { getEffectivePermissions } = require('~/server/services/PermissionService'); -const { validateAgentApiKey, findUser } = require('~/models'); const { configMiddleware } = require('~/server/middleware'); -const { getRoleByName } = require('~/models/Role'); -const { getAgent } = require('~/models/Agent'); +const db = require('~/models'); const router = express.Router(); const requireApiKeyAuth = createRequireApiKeyAuth({ - validateAgentApiKey, - findUser, + validateAgentApiKey: db.validateAgentApiKey, + findUser: db.findUser, }); const checkRemoteAgentsFeature = generateCheckAccess({ permissionType: PermissionTypes.REMOTE_AGENTS, permissions: [Permissions.USE], - getRoleByName, + getRoleByName: db.getRoleByName, }); const checkAgentPermission = createCheckRemoteAgentAccess({ - getAgent, + getAgent: db.getAgent, getEffectivePermissions, }); diff --git a/api/server/routes/agents/v1.js b/api/server/routes/agents/v1.js index 682a9c795f..c4f90d0bd5 100644 --- a/api/server/routes/agents/v1.js +++ b/api/server/routes/agents/v1.js @@ -3,7 +3,7 @@ const { generateCheckAccess } = require('@librechat/api'); const { PermissionTypes, Permissions, PermissionBits } = require('librechat-data-provider'); const { requireJwtAuth, configMiddleware, canAccessAgentResource } = require('~/server/middleware'); const v1 = require('~/server/controllers/agents/v1'); -const { getRoleByName } = require('~/models/Role'); +const { getRoleByName } = require('~/models'); const actions = require('./actions'); const tools = require('./tools'); @@ -21,15 +21,6 @@ const checkAgentCreate = generateCheckAccess({ getRoleByName, }); -const checkGlobalAgentShare = generateCheckAccess({ - permissionType: PermissionTypes.AGENTS, - permissions: [Permissions.USE, Permissions.CREATE], - bodyProps: { - [Permissions.SHARE]: ['projectIds', 'removeProjectIds'], - }, - getRoleByName, -}); - router.use(requireJwtAuth); /** @@ -99,7 +90,7 @@ router.get( */ router.patch( '/:id', - checkGlobalAgentShare, + checkAgentCreate, canAccessAgentResource({ requiredPermission: PermissionBits.EDIT, resourceIdParam: 'id', @@ -117,7 +108,7 @@ router.post( '/:id/duplicate', checkAgentCreate, canAccessAgentResource({ - requiredPermission: PermissionBits.VIEW, + requiredPermission: PermissionBits.EDIT, resourceIdParam: 'id', }), v1.duplicateAgent, @@ -148,7 +139,7 @@ router.delete( */ router.post( '/:id/revert', - checkGlobalAgentShare, + checkAgentCreate, canAccessAgentResource({ requiredPermission: PermissionBits.EDIT, resourceIdParam: 'id', diff --git a/api/server/routes/apiKeys.js b/api/server/routes/apiKeys.js index 29dcc326f5..ee11a8b0dd 100644 --- a/api/server/routes/apiKeys.js +++ b/api/server/routes/apiKeys.js @@ -6,9 +6,9 @@ const { createAgentApiKey, deleteAgentApiKey, listAgentApiKeys, + getRoleByName, } = require('~/models'); const { requireJwtAuth } = require('~/server/middleware'); -const { getRoleByName } = require('~/models/Role'); const router = express.Router(); diff --git a/api/server/routes/assistants/actions.js b/api/server/routes/assistants/actions.js index 57975d32a7..977d3f92a7 100644 --- a/api/server/routes/assistants/actions.js +++ b/api/server/routes/assistants/actions.js @@ -3,10 +3,13 @@ const { nanoid } = require('nanoid'); const { logger } = require('@librechat/data-schemas'); const { isActionDomainAllowed } = require('@librechat/api'); const { actionDelimiter, EModelEndpoint, removeNullishValues } = require('librechat-data-provider'); -const { encryptMetadata, domainParser } = require('~/server/services/ActionService'); +const { + legacyDomainEncode, + encryptMetadata, + domainParser, +} = require('~/server/services/ActionService'); const { getOpenAIClient } = require('~/server/controllers/assistants/helpers'); -const { updateAction, getActions, deleteAction } = require('~/models/Action'); -const { updateAssistantDoc, getAssistant } = require('~/models/Assistant'); +const db = require('~/models'); const router = express.Router(); @@ -39,27 +42,31 @@ router.post('/:assistant_id', async (req, res) => { return res.status(400).json({ message: 'Domain not allowed' }); } - let { domain } = metadata; - domain = await domainParser(domain, true); + const encodedDomain = await domainParser(metadata.domain, true); - if (!domain) { + if (!encodedDomain) { return res.status(400).json({ message: 'No domain provided' }); } + const legacyDomain = legacyDomainEncode(metadata.domain); + const action_id = _action_id ?? nanoid(); const initialPromises = []; const { openai } = await getOpenAIClient({ req, res }); - initialPromises.push(getAssistant({ assistant_id })); + initialPromises.push(db.getAssistant({ assistant_id })); initialPromises.push(openai.beta.assistants.retrieve(assistant_id)); - !!_action_id && initialPromises.push(getActions({ action_id }, true)); + !!_action_id && initialPromises.push(db.getActions({ action_id }, true)); /** @type {[AssistantDocument, Assistant, [Action|undefined]]} */ const [assistant_data, assistant, actions_result] = await Promise.all(initialPromises); if (actions_result && actions_result.length) { const action = actions_result[0]; + if (action.assistant_id !== assistant_id) { + return res.status(403).json({ message: 'Action does not belong to this assistant' }); + } metadata = { ...action.metadata, ...metadata }; } @@ -78,25 +85,29 @@ router.post('/:assistant_id', async (req, res) => { actions.push(action); } - actions.push(`${domain}${actionDelimiter}${action_id}`); + actions.push(`${encodedDomain}${actionDelimiter}${action_id}`); /** @type {{ tools: FunctionTool[] | { type: 'code_interpreter'|'retrieval'}[]}} */ const { tools: _tools = [] } = assistant; + const shouldRemoveAssistantTool = (tool) => { + if (!tool.function) { + return false; + } + const name = tool.function.name; + return ( + name.includes(encodedDomain) || name.includes(legacyDomain) || name.includes(action_id) + ); + }; + const tools = _tools - .filter( - (tool) => - !( - tool.function && - (tool.function.name.includes(domain) || tool.function.name.includes(action_id)) - ), - ) + .filter((tool) => !shouldRemoveAssistantTool(tool)) .concat( functions.map((tool) => ({ ...tool, function: { ...tool.function, - name: `${tool.function.name}${actionDelimiter}${domain}`, + name: `${tool.function.name}${actionDelimiter}${encodedDomain}`, }, })), ); @@ -109,7 +120,7 @@ router.post('/:assistant_id', async (req, res) => { if (!assistant_data) { assistantUpdateData.user = req.user.id; } - promises.push(updateAssistantDoc({ assistant_id }, assistantUpdateData)); + promises.push(db.updateAssistantDoc({ assistant_id }, assistantUpdateData)); // Only update user field for new actions const actionUpdateData = { metadata, assistant_id }; @@ -117,7 +128,7 @@ router.post('/:assistant_id', async (req, res) => { // For new actions, use the assistant owner's user ID actionUpdateData.user = assistant_user || req.user.id; } - promises.push(updateAction({ action_id }, actionUpdateData)); + promises.push(db.updateAction({ action_id, assistant_id }, actionUpdateData)); /** @type {[AssistantDocument, Action]} */ let [assistantDocument, updatedAction] = await Promise.all(promises); @@ -159,7 +170,7 @@ router.delete('/:assistant_id/:action_id/:model', async (req, res) => { const { openai } = await getOpenAIClient({ req, res }); const initialPromises = []; - initialPromises.push(getAssistant({ assistant_id })); + initialPromises.push(db.getAssistant({ assistant_id })); initialPromises.push(openai.beta.assistants.retrieve(assistant_id)); /** @type {[AssistantDocument, Assistant]} */ @@ -168,23 +179,25 @@ router.delete('/:assistant_id/:action_id/:model', async (req, res) => { const { actions = [] } = assistant_data ?? {}; const { tools = [] } = assistant ?? {}; - let domain = ''; + let storedDomain = ''; const updatedActions = actions.filter((action) => { if (action.includes(action_id)) { - [domain] = action.split(actionDelimiter); + [storedDomain] = action.split(actionDelimiter); return false; } return true; }); - domain = await domainParser(domain, true); - - if (!domain) { + if (!storedDomain) { return res.status(400).json({ message: 'No domain provided' }); } const updatedTools = tools.filter( - (tool) => !(tool.function && tool.function.name.includes(domain)), + (tool) => + !( + tool.function && + (tool.function.name.includes(storedDomain) || tool.function.name.includes(action_id)) + ), ); await openai.beta.assistants.update(assistant_id, { tools: updatedTools }); @@ -195,10 +208,16 @@ router.delete('/:assistant_id/:action_id/:model', async (req, res) => { if (!assistant_data) { assistantUpdateData.user = req.user.id; } - promises.push(updateAssistantDoc({ assistant_id }, assistantUpdateData)); - promises.push(deleteAction({ action_id })); + promises.push(db.updateAssistantDoc({ assistant_id }, assistantUpdateData)); + promises.push(db.deleteAction({ action_id, assistant_id })); - await Promise.all(promises); + const [, deletedAction] = await Promise.all(promises); + if (!deletedAction) { + logger.warn('[Assistant Action Delete] No matching action document found', { + action_id, + assistant_id, + }); + } res.status(200).json({ message: 'Action deleted successfully' }); } catch (error) { const message = 'Trouble deleting the Assistant Action'; diff --git a/api/server/routes/auth.js b/api/server/routes/auth.js index e84442f65f..c660e6f99d 100644 --- a/api/server/routes/auth.js +++ b/api/server/routes/auth.js @@ -17,13 +17,14 @@ const { const { verify2FAWithTempToken } = require('~/server/controllers/auth/TwoFactorAuthController'); const { logoutController } = require('~/server/controllers/auth/LogoutController'); const { loginController } = require('~/server/controllers/auth/LoginController'); +const { findBalanceByUser, upsertBalanceFields } = require('~/models'); const { getAppConfig } = require('~/server/services/Config'); const middleware = require('~/server/middleware'); -const { Balance } = require('~/db/models'); const setBalanceConfig = createSetBalanceConfig({ getAppConfig, - Balance, + findBalanceByUser, + upsertBalanceFields, }); const router = express.Router(); @@ -63,7 +64,7 @@ router.post( resetPasswordController, ); -router.get('/2fa/enable', middleware.requireJwtAuth, enable2FA); +router.post('/2fa/enable', middleware.requireJwtAuth, enable2FA); router.post('/2fa/verify', middleware.requireJwtAuth, verify2FA); router.post('/2fa/verify-temp', middleware.checkBan, verify2FAWithTempToken); router.post('/2fa/confirm', middleware.requireJwtAuth, confirm2FA); diff --git a/api/server/routes/banner.js b/api/server/routes/banner.js index cf7eafd017..ad949fd2ca 100644 --- a/api/server/routes/banner.js +++ b/api/server/routes/banner.js @@ -1,13 +1,15 @@ const express = require('express'); - -const { getBanner } = require('~/models/Banner'); +const { logger } = require('@librechat/data-schemas'); const optionalJwtAuth = require('~/server/middleware/optionalJwtAuth'); +const { getBanner } = require('~/models'); + const router = express.Router(); router.get('/', optionalJwtAuth, async (req, res) => { try { res.status(200).send(await getBanner(req.user)); } catch (error) { + logger.error('[getBanner] Error getting banner', error); res.status(500).json({ message: 'Error getting banner' }); } }); diff --git a/api/server/routes/categories.js b/api/server/routes/categories.js index da1828b3ce..612bc37860 100644 --- a/api/server/routes/categories.js +++ b/api/server/routes/categories.js @@ -1,7 +1,7 @@ const express = require('express'); const router = express.Router(); const { requireJwtAuth } = require('~/server/middleware'); -const { getCategories } = require('~/models/Categories'); +const { getCategories } = require('~/models'); router.get('/', requireJwtAuth, async (req, res) => { try { diff --git a/api/server/routes/config.js b/api/server/routes/config.js index a2dc5b79d2..bf60f57e08 100644 --- a/api/server/routes/config.js +++ b/api/server/routes/config.js @@ -1,10 +1,9 @@ const express = require('express'); const { logger } = require('@librechat/data-schemas'); const { isEnabled, getBalanceConfig } = require('@librechat/api'); -const { Constants, CacheKeys, defaultSocialLogins } = require('librechat-data-provider'); +const { CacheKeys, defaultSocialLogins } = require('librechat-data-provider'); const { getLdapConfig } = require('~/server/services/Config/ldap'); const { getAppConfig } = require('~/server/services/Config/app'); -const { getProjectByName } = require('~/models/Project'); const { getLogStores } = require('~/cache'); const router = express.Router(); @@ -16,9 +15,7 @@ const sharedLinksEnabled = process.env.ALLOW_SHARED_LINKS === undefined || isEnabled(process.env.ALLOW_SHARED_LINKS); const publicSharedLinksEnabled = - sharedLinksEnabled && - (process.env.ALLOW_SHARED_LINKS_PUBLIC === undefined || - isEnabled(process.env.ALLOW_SHARED_LINKS_PUBLIC)); + sharedLinksEnabled && isEnabled(process.env.ALLOW_SHARED_LINKS_PUBLIC); const sharePointFilePickerEnabled = isEnabled(process.env.ENABLE_SHAREPOINT_FILEPICKER); const openidReuseTokens = isEnabled(process.env.OPENID_REUSE_TOKENS); @@ -37,8 +34,6 @@ router.get('/', async function (req, res) { return today.getMonth() === 1 && today.getDate() === 11; }; - const instanceProject = await getProjectByName(Constants.GLOBAL_PROJECT_NAME, '_id'); - const ldap = getLdapConfig(); try { @@ -101,7 +96,6 @@ router.get('/', async function (req, res) { sharedLinksEnabled, publicSharedLinksEnabled, analyticsGtmId: process.env.ANALYTICS_GTM_ID, - instanceProjectId: instanceProject._id.toString(), bundlerURL: process.env.SANDPACK_BUNDLER_URL, staticBundlerURL: process.env.SANDPACK_STATIC_BUNDLER_URL, sharePointFilePickerEnabled, diff --git a/api/server/routes/convos.js b/api/server/routes/convos.js index bb9c4ebea9..1964075ed3 100644 --- a/api/server/routes/convos.js +++ b/api/server/routes/convos.js @@ -1,7 +1,7 @@ const multer = require('multer'); const express = require('express'); const { sleep } = require('@librechat/agents'); -const { isEnabled } = require('@librechat/api'); +const { isEnabled, resolveImportMaxFileSize } = require('@librechat/api'); const { logger } = require('@librechat/data-schemas'); const { CacheKeys, EModelEndpoint } = require('librechat-data-provider'); const { @@ -10,14 +10,12 @@ const { createForkLimiters, configMiddleware, } = require('~/server/middleware'); -const { getConvosByCursor, deleteConvos, getConvo, saveConvo } = require('~/models/Conversation'); const { forkConversation, duplicateConversation } = require('~/server/utils/import/fork'); const { storage, importFileFilter } = require('~/server/routes/files/multer'); -const { deleteAllSharedLinks, deleteConvoSharedLink } = require('~/models'); const requireJwtAuth = require('~/server/middleware/requireJwtAuth'); const { importConversations } = require('~/server/utils/import'); -const { deleteToolCalls } = require('~/models/ToolCall'); const getLogStores = require('~/cache/getLogStores'); +const db = require('~/models'); const assistantClients = { [EModelEndpoint.azureAssistants]: require('~/server/services/Endpoints/azureAssistants'), @@ -41,7 +39,7 @@ router.get('/', async (req, res) => { } try { - const result = await getConvosByCursor(req.user.id, { + const result = await db.getConvosByCursor(req.user.id, { cursor, limit, isArchived, @@ -59,7 +57,7 @@ router.get('/', async (req, res) => { router.get('/:conversationId', async (req, res) => { const { conversationId } = req.params; - const convo = await getConvo(req.user.id, conversationId); + const convo = await db.getConvo(req.user.id, conversationId); if (convo) { res.status(200).json(convo); @@ -128,10 +126,10 @@ router.delete('/', async (req, res) => { } try { - const dbResponse = await deleteConvos(req.user.id, filter); + const dbResponse = await db.deleteConvos(req.user.id, filter); if (filter.conversationId) { - await deleteToolCalls(req.user.id, filter.conversationId); - await deleteConvoSharedLink(req.user.id, filter.conversationId); + await db.deleteToolCalls(req.user.id, filter.conversationId); + await db.deleteConvoSharedLink(req.user.id, filter.conversationId); } res.status(201).json(dbResponse); } catch (error) { @@ -142,9 +140,9 @@ router.delete('/', async (req, res) => { router.delete('/all', async (req, res) => { try { - const dbResponse = await deleteConvos(req.user.id, {}); - await deleteToolCalls(req.user.id); - await deleteAllSharedLinks(req.user.id); + const dbResponse = await db.deleteConvos(req.user.id, {}); + await db.deleteToolCalls(req.user.id); + await db.deleteAllSharedLinks(req.user.id); res.status(201).json(dbResponse); } catch (error) { logger.error('Error clearing conversations', error); @@ -171,8 +169,12 @@ router.post('/archive', validateConvoAccess, async (req, res) => { } try { - const dbResponse = await saveConvo( - req, + const dbResponse = await db.saveConvo( + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { conversationId, isArchived }, { context: `POST /api/convos/archive ${conversationId}` }, ); @@ -211,8 +213,12 @@ router.post('/update', validateConvoAccess, async (req, res) => { const sanitizedTitle = title.trim().slice(0, MAX_CONVO_TITLE_LENGTH); try { - const dbResponse = await saveConvo( - req, + const dbResponse = await db.saveConvo( + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { conversationId, title: sanitizedTitle }, { context: `POST /api/convos/update ${conversationId}` }, ); @@ -224,8 +230,27 @@ router.post('/update', validateConvoAccess, async (req, res) => { }); const { importIpLimiter, importUserLimiter } = createImportLimiters(); +/** Fork and duplicate share one rate-limit budget (same "clone" operation class) */ const { forkIpLimiter, forkUserLimiter } = createForkLimiters(); -const upload = multer({ storage: storage, fileFilter: importFileFilter }); +const importMaxFileSize = resolveImportMaxFileSize(); +const upload = multer({ + storage, + fileFilter: importFileFilter, + limits: { fileSize: importMaxFileSize }, +}); +const uploadSingle = upload.single('file'); + +function handleUpload(req, res, next) { + uploadSingle(req, res, (err) => { + if (err && err.code === 'LIMIT_FILE_SIZE') { + return res.status(413).json({ message: 'File exceeds the maximum allowed size' }); + } + if (err) { + return next(err); + } + next(); + }); +} /** * Imports a conversation from a JSON file and saves it to the database. @@ -238,7 +263,7 @@ router.post( importIpLimiter, importUserLimiter, configMiddleware, - upload.single('file'), + handleUpload, async (req, res) => { try { /* TODO: optimize to return imported conversations and add manually */ @@ -280,7 +305,7 @@ router.post('/fork', forkIpLimiter, forkUserLimiter, async (req, res) => { } }); -router.post('/duplicate', async (req, res) => { +router.post('/duplicate', forkIpLimiter, forkUserLimiter, async (req, res) => { const { conversationId, title } = req.body; try { diff --git a/api/server/routes/files/files.agents.test.js b/api/server/routes/files/files.agents.test.js index 7c21e95234..5a01df022d 100644 --- a/api/server/routes/files/files.agents.test.js +++ b/api/server/routes/files/files.agents.test.js @@ -10,8 +10,8 @@ const { ResourceType, PrincipalType, } = require('librechat-data-provider'); -const { createAgent } = require('~/models/Agent'); -const { createFile } = require('~/models'); +const { SystemCapabilities } = require('@librechat/data-schemas'); +const { createAgent, createFile } = require('~/models'); // Only mock the external dependencies that we don't want to test jest.mock('~/server/services/Files/process', () => ({ @@ -39,7 +39,16 @@ jest.mock('~/server/services/Tools/credentials', () => ({ loadAuthValues: jest.fn(), })); -jest.mock('~/server/services/Files/S3/crud', () => ({ +jest.mock('sharp', () => + jest.fn(() => ({ + metadata: jest.fn().mockResolvedValue({}), + toFormat: jest.fn().mockReturnThis(), + toBuffer: jest.fn().mockResolvedValue(Buffer.alloc(0)), + })), +); + +jest.mock('@librechat/api', () => ({ + ...jest.requireActual('@librechat/api'), refreshS3FileUrls: jest.fn(), })); @@ -83,6 +92,7 @@ describe('File Routes - Agent Files Endpoint', () => { let AclEntry; // eslint-disable-next-line no-unused-vars let AccessRole; + let SystemGrant; let modelsToCleanup = []; beforeAll(async () => { @@ -109,6 +119,7 @@ describe('File Routes - Agent Files Endpoint', () => { AclEntry = models.AclEntry; User = models.User; AccessRole = models.AccessRole; + SystemGrant = models.SystemGrant; // Seed default roles using our methods await methods.seedDefaultRoles(); @@ -533,7 +544,7 @@ describe('File Routes - Agent Files Endpoint', () => { expect(processAgentFileUpload).not.toHaveBeenCalled(); }); - it('should allow file upload for admin user regardless of agent ownership', async () => { + it('should allow file upload for user with MANAGE_AGENTS capability regardless of agent ownership', async () => { // Create an agent owned by authorId await createAgent({ id: agentCustomId, @@ -543,6 +554,14 @@ describe('File Routes - Agent Files Endpoint', () => { author: authorId, }); + // Seed MANAGE_AGENTS capability for the ADMIN role + await SystemGrant.create({ + principalType: PrincipalType.ROLE, + principalId: SystemRoles.ADMIN, + capability: SystemCapabilities.MANAGE_AGENTS, + grantedAt: new Date(), + }); + // Create app with admin user (otherUserId as admin) const testApp = createAppWithUser(otherUserId, SystemRoles.ADMIN); diff --git a/api/server/routes/files/files.js b/api/server/routes/files/files.js index 5de2ddb379..bc79a0fb96 100644 --- a/api/server/routes/files/files.js +++ b/api/server/routes/files/files.js @@ -2,18 +2,19 @@ const fs = require('fs').promises; const express = require('express'); const { EnvVar } = require('@librechat/agents'); const { logger } = require('@librechat/data-schemas'); +const { refreshS3FileUrls, verifyAgentUploadPermission } = require('@librechat/api'); const { Time, isUUID, CacheKeys, FileSources, - SystemRoles, ResourceType, EModelEndpoint, PermissionBits, checkOpenAIStorage, isAssistantsEndpoint, } = require('librechat-data-provider'); +const { SystemCapabilities } = require('@librechat/data-schemas'); const { filterFile, processFileUpload, @@ -25,27 +26,25 @@ const { getStrategyFunctions } = require('~/server/services/Files/strategies'); const { getOpenAIClient } = require('~/server/controllers/assistants/helpers'); const { checkPermission } = require('~/server/services/PermissionService'); const { loadAuthValues } = require('~/server/services/Tools/credentials'); -const { refreshS3FileUrls } = require('~/server/services/Files/S3/crud'); const { hasAccessToFilesViaAgent } = require('~/server/services/Files'); -const { getFiles, batchUpdateFiles } = require('~/models'); const { cleanFileName } = require('~/server/utils/files'); -const { getAssistant } = require('~/models/Assistant'); -const { getAgent } = require('~/models/Agent'); +const { hasCapability } = require('~/server/middleware'); const { getLogStores } = require('~/cache'); const { Readable } = require('stream'); +const db = require('~/models'); const router = express.Router(); router.get('/', async (req, res) => { try { const appConfig = req.config; - const files = await getFiles({ user: req.user.id }); + const files = await db.getFiles({ user: req.user.id }); if (appConfig.fileStrategy === FileSources.s3) { try { const cache = getLogStores(CacheKeys.S3_EXPIRY_INTERVAL); const alreadyChecked = await cache.get(req.user.id); if (!alreadyChecked) { - await refreshS3FileUrls(files, batchUpdateFiles); + await refreshS3FileUrls(files, db.batchUpdateFiles); await cache.set(req.user.id, true, Time.THIRTY_MINUTES); } } catch (error) { @@ -74,7 +73,7 @@ router.get('/agent/:agent_id', async (req, res) => { return res.status(400).json({ error: 'Agent ID is required' }); } - const agent = await getAgent({ id: agent_id }); + const agent = await db.getAgent({ id: agent_id }); if (!agent) { return res.status(200).json([]); } @@ -106,7 +105,7 @@ router.get('/agent/:agent_id', async (req, res) => { return res.status(200).json([]); } - const files = await getFiles({ file_id: { $in: agentFileIds } }, null, { text: 0 }); + const files = await db.getFiles({ file_id: { $in: agentFileIds } }, null, { text: 0 }); res.status(200).json(files); } catch (error) { @@ -151,7 +150,7 @@ router.delete('/', async (req, res) => { } const fileIds = files.map((file) => file.file_id); - const dbFiles = await getFiles({ file_id: { $in: fileIds } }); + const dbFiles = await db.getFiles({ file_id: { $in: fileIds } }); const ownedFiles = []; const nonOwnedFiles = []; @@ -209,7 +208,7 @@ router.delete('/', async (req, res) => { /* Handle agent unlinking even if no valid files to delete */ if (req.body.agent_id && req.body.tool_resource && dbFiles.length === 0) { - const agent = await getAgent({ + const agent = await db.getAgent({ id: req.body.agent_id, }); @@ -223,7 +222,7 @@ router.delete('/', async (req, res) => { /* Handle assistant unlinking even if no valid files to delete */ if (req.body.assistant_id && req.body.tool_resource && dbFiles.length === 0) { - const assistant = await getAssistant({ + const assistant = await db.getAssistant({ id: req.body.assistant_id, }); @@ -381,47 +380,23 @@ router.post('/', async (req, res) => { return await processFileUpload({ req, res, metadata }); } - /** - * Check agent permissions for permanent agent file uploads (not message attachments). - * Message attachments (message_file=true) are temporary files for a single conversation - * and should be allowed for users who can chat with the agent. - * Permanent file uploads to tool_resources require EDIT permission. - */ - const isMessageAttachment = metadata.message_file === true || metadata.message_file === 'true'; - if (metadata.agent_id && metadata.tool_resource && !isMessageAttachment) { - const userId = req.user.id; + let skipUploadAuth = false; + try { + skipUploadAuth = await hasCapability(req.user, SystemCapabilities.MANAGE_AGENTS); + } catch (err) { + logger.warn(`[/files] capability check failed, denying bypass: ${err.message}`); + } - /** Admin users bypass permission checks */ - if (req.user.role !== SystemRoles.ADMIN) { - const agent = await getAgent({ id: metadata.agent_id }); - - if (!agent) { - return res.status(404).json({ - error: 'Not Found', - message: 'Agent not found', - }); - } - - /** Check if user is the author or has edit permission */ - if (agent.author.toString() !== userId) { - const hasEditPermission = await checkPermission({ - userId, - role: req.user.role, - resourceType: ResourceType.AGENT, - resourceId: agent._id, - requiredPermission: PermissionBits.EDIT, - }); - - if (!hasEditPermission) { - logger.warn( - `[/files] User ${userId} denied upload to agent ${metadata.agent_id} (insufficient permissions)`, - ); - return res.status(403).json({ - error: 'Forbidden', - message: 'Insufficient permissions to upload files to this agent', - }); - } - } + if (!skipUploadAuth) { + const denied = await verifyAgentUploadPermission({ + req, + res, + metadata, + getAgent: db.getAgent, + checkPermission, + }); + if (denied) { + return; } } diff --git a/api/server/routes/files/files.test.js b/api/server/routes/files/files.test.js index 1d548b44be..37cbf68b93 100644 --- a/api/server/routes/files/files.test.js +++ b/api/server/routes/files/files.test.js @@ -10,8 +10,7 @@ const { AccessRoleIds, PrincipalType, } = require('librechat-data-provider'); -const { createAgent } = require('~/models/Agent'); -const { createFile } = require('~/models'); +const { createAgent, createFile } = require('~/models'); // Only mock the external dependencies that we don't want to test jest.mock('~/server/services/Files/process', () => ({ @@ -33,7 +32,16 @@ jest.mock('~/server/services/Tools/credentials', () => ({ loadAuthValues: jest.fn(), })); -jest.mock('~/server/services/Files/S3/crud', () => ({ +jest.mock('sharp', () => + jest.fn(() => ({ + metadata: jest.fn().mockResolvedValue({}), + toFormat: jest.fn().mockReturnThis(), + toBuffer: jest.fn().mockResolvedValue(Buffer.alloc(0)), + })), +); + +jest.mock('@librechat/api', () => ({ + ...jest.requireActual('@librechat/api'), refreshS3FileUrls: jest.fn(), })); diff --git a/api/server/routes/files/images.agents.test.js b/api/server/routes/files/images.agents.test.js new file mode 100644 index 0000000000..f855a436d4 --- /dev/null +++ b/api/server/routes/files/images.agents.test.js @@ -0,0 +1,376 @@ +const express = require('express'); +const request = require('supertest'); +const mongoose = require('mongoose'); +const { v4: uuidv4 } = require('uuid'); +const { createMethods } = require('@librechat/data-schemas'); +const { MongoMemoryServer } = require('mongodb-memory-server'); +const { + SystemRoles, + AccessRoleIds, + ResourceType, + PrincipalType, +} = require('librechat-data-provider'); +const { createAgent } = require('~/models'); + +jest.mock('~/server/services/Files/process', () => ({ + processAgentFileUpload: jest.fn().mockImplementation(async ({ res }) => { + return res.status(200).json({ message: 'Agent file uploaded', file_id: 'test-file-id' }); + }), + processImageFile: jest.fn().mockImplementation(async ({ res }) => { + return res.status(200).json({ message: 'Image processed' }); + }), + filterFile: jest.fn(), +})); + +jest.mock('fs', () => { + const actualFs = jest.requireActual('fs'); + return { + ...actualFs, + promises: { + ...actualFs.promises, + unlink: jest.fn().mockResolvedValue(undefined), + }, + }; +}); + +const fs = require('fs'); +const { processAgentFileUpload } = require('~/server/services/Files/process'); + +const router = require('~/server/routes/files/images'); + +describe('POST /images - Agent Upload Permission Check (Integration)', () => { + let mongoServer; + let authorId; + let otherUserId; + let agentCustomId; + let User; + let Agent; + let AclEntry; + let methods; + let modelsToCleanup = []; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = mongoServer.getUri(); + await mongoose.connect(mongoUri); + + const { createModels } = require('@librechat/data-schemas'); + const models = createModels(mongoose); + modelsToCleanup = Object.keys(models); + Object.assign(mongoose.models, models); + methods = createMethods(mongoose); + + User = models.User; + Agent = models.Agent; + AclEntry = models.AclEntry; + + await methods.seedDefaultRoles(); + }); + + afterAll(async () => { + const collections = mongoose.connection.collections; + for (const key in collections) { + await collections[key].deleteMany({}); + } + for (const modelName of modelsToCleanup) { + if (mongoose.models[modelName]) { + delete mongoose.models[modelName]; + } + } + await mongoose.disconnect(); + await mongoServer.stop(); + }); + + beforeEach(async () => { + await Agent.deleteMany({}); + await User.deleteMany({}); + await AclEntry.deleteMany({}); + + authorId = new mongoose.Types.ObjectId(); + otherUserId = new mongoose.Types.ObjectId(); + agentCustomId = `agent_${uuidv4().replace(/-/g, '').substring(0, 21)}`; + + await User.create({ _id: authorId, username: 'author', email: 'author@test.com' }); + await User.create({ _id: otherUserId, username: 'other', email: 'other@test.com' }); + + jest.clearAllMocks(); + }); + + const createAppWithUser = (userId, userRole = SystemRoles.USER) => { + const app = express(); + app.use(express.json()); + app.use((req, _res, next) => { + if (req.method === 'POST') { + req.file = { + originalname: 'test.png', + mimetype: 'image/png', + size: 100, + path: '/tmp/t.png', + filename: 'test.png', + }; + req.file_id = uuidv4(); + } + next(); + }); + app.use((req, _res, next) => { + req.user = { id: userId.toString(), role: userRole }; + req.app = { locals: {} }; + req.config = { fileStrategy: 'local', paths: { imageOutput: '/tmp/images' } }; + next(); + }); + app.use('/images', router); + return app; + }; + + it('should return 403 when user has no permission on agent', async () => { + await createAgent({ + id: agentCustomId, + name: 'Test Agent', + provider: 'openai', + model: 'gpt-4', + author: authorId, + }); + + const app = createAppWithUser(otherUserId); + const response = await request(app).post('/images').send({ + endpoint: 'agents', + agent_id: agentCustomId, + tool_resource: 'context', + file_id: uuidv4(), + }); + + expect(response.status).toBe(403); + expect(response.body.error).toBe('Forbidden'); + expect(processAgentFileUpload).not.toHaveBeenCalled(); + expect(fs.promises.unlink).toHaveBeenCalledWith('/tmp/t.png'); + }); + + it('should allow upload for agent owner', async () => { + await createAgent({ + id: agentCustomId, + name: 'Test Agent', + provider: 'openai', + model: 'gpt-4', + author: authorId, + }); + + const app = createAppWithUser(authorId); + const response = await request(app).post('/images').send({ + endpoint: 'agents', + agent_id: agentCustomId, + tool_resource: 'context', + file_id: uuidv4(), + }); + + expect(response.status).toBe(200); + expect(processAgentFileUpload).toHaveBeenCalled(); + }); + + it('should allow upload for admin regardless of ownership', async () => { + await createAgent({ + id: agentCustomId, + name: 'Test Agent', + provider: 'openai', + model: 'gpt-4', + author: authorId, + }); + + const app = createAppWithUser(otherUserId, SystemRoles.ADMIN); + const response = await request(app).post('/images').send({ + endpoint: 'agents', + agent_id: agentCustomId, + tool_resource: 'context', + file_id: uuidv4(), + }); + + expect(response.status).toBe(200); + expect(processAgentFileUpload).toHaveBeenCalled(); + }); + + it('should allow upload for user with EDIT permission', async () => { + const agent = await createAgent({ + id: agentCustomId, + name: 'Test Agent', + provider: 'openai', + model: 'gpt-4', + author: authorId, + }); + + const { grantPermission } = require('~/server/services/PermissionService'); + await grantPermission({ + principalType: PrincipalType.USER, + principalId: otherUserId, + resourceType: ResourceType.AGENT, + resourceId: agent._id, + accessRoleId: AccessRoleIds.AGENT_EDITOR, + grantedBy: authorId, + }); + + const app = createAppWithUser(otherUserId); + const response = await request(app).post('/images').send({ + endpoint: 'agents', + agent_id: agentCustomId, + tool_resource: 'context', + file_id: uuidv4(), + }); + + expect(response.status).toBe(200); + expect(processAgentFileUpload).toHaveBeenCalled(); + }); + + it('should deny upload for user with only VIEW permission', async () => { + const agent = await createAgent({ + id: agentCustomId, + name: 'Test Agent', + provider: 'openai', + model: 'gpt-4', + author: authorId, + }); + + const { grantPermission } = require('~/server/services/PermissionService'); + await grantPermission({ + principalType: PrincipalType.USER, + principalId: otherUserId, + resourceType: ResourceType.AGENT, + resourceId: agent._id, + accessRoleId: AccessRoleIds.AGENT_VIEWER, + grantedBy: authorId, + }); + + const app = createAppWithUser(otherUserId); + const response = await request(app).post('/images').send({ + endpoint: 'agents', + agent_id: agentCustomId, + tool_resource: 'context', + file_id: uuidv4(), + }); + + expect(response.status).toBe(403); + expect(response.body.error).toBe('Forbidden'); + expect(processAgentFileUpload).not.toHaveBeenCalled(); + expect(fs.promises.unlink).toHaveBeenCalledWith('/tmp/t.png'); + }); + + it('should skip permission check for regular image uploads without agent_id/tool_resource', async () => { + const app = createAppWithUser(otherUserId); + const response = await request(app).post('/images').send({ + endpoint: 'agents', + file_id: uuidv4(), + }); + + expect(response.status).toBe(200); + }); + + it('should return 404 for non-existent agent', async () => { + const app = createAppWithUser(otherUserId); + const response = await request(app).post('/images').send({ + endpoint: 'agents', + agent_id: 'agent_nonexistent123456789', + tool_resource: 'context', + file_id: uuidv4(), + }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Not Found'); + expect(processAgentFileUpload).not.toHaveBeenCalled(); + expect(fs.promises.unlink).toHaveBeenCalledWith('/tmp/t.png'); + }); + + it('should allow message_file attachment (boolean true) without EDIT permission', async () => { + const agent = await createAgent({ + id: agentCustomId, + name: 'Test Agent', + provider: 'openai', + model: 'gpt-4', + author: authorId, + }); + + const { grantPermission } = require('~/server/services/PermissionService'); + await grantPermission({ + principalType: PrincipalType.USER, + principalId: otherUserId, + resourceType: ResourceType.AGENT, + resourceId: agent._id, + accessRoleId: AccessRoleIds.AGENT_VIEWER, + grantedBy: authorId, + }); + + const app = createAppWithUser(otherUserId); + const response = await request(app).post('/images').send({ + endpoint: 'agents', + agent_id: agentCustomId, + tool_resource: 'context', + message_file: true, + file_id: uuidv4(), + }); + + expect(response.status).toBe(200); + expect(processAgentFileUpload).toHaveBeenCalled(); + }); + + it('should allow message_file attachment (string "true") without EDIT permission', async () => { + const agent = await createAgent({ + id: agentCustomId, + name: 'Test Agent', + provider: 'openai', + model: 'gpt-4', + author: authorId, + }); + + const { grantPermission } = require('~/server/services/PermissionService'); + await grantPermission({ + principalType: PrincipalType.USER, + principalId: otherUserId, + resourceType: ResourceType.AGENT, + resourceId: agent._id, + accessRoleId: AccessRoleIds.AGENT_VIEWER, + grantedBy: authorId, + }); + + const app = createAppWithUser(otherUserId); + const response = await request(app).post('/images').send({ + endpoint: 'agents', + agent_id: agentCustomId, + tool_resource: 'context', + message_file: 'true', + file_id: uuidv4(), + }); + + expect(response.status).toBe(200); + expect(processAgentFileUpload).toHaveBeenCalled(); + }); + + it('should deny upload when message_file is false (not a message attachment)', async () => { + const agent = await createAgent({ + id: agentCustomId, + name: 'Test Agent', + provider: 'openai', + model: 'gpt-4', + author: authorId, + }); + + const { grantPermission } = require('~/server/services/PermissionService'); + await grantPermission({ + principalType: PrincipalType.USER, + principalId: otherUserId, + resourceType: ResourceType.AGENT, + resourceId: agent._id, + accessRoleId: AccessRoleIds.AGENT_VIEWER, + grantedBy: authorId, + }); + + const app = createAppWithUser(otherUserId); + const response = await request(app).post('/images').send({ + endpoint: 'agents', + agent_id: agentCustomId, + tool_resource: 'context', + message_file: false, + file_id: uuidv4(), + }); + + expect(response.status).toBe(403); + expect(response.body.error).toBe('Forbidden'); + expect(processAgentFileUpload).not.toHaveBeenCalled(); + expect(fs.promises.unlink).toHaveBeenCalledWith('/tmp/t.png'); + }); +}); diff --git a/api/server/routes/files/images.js b/api/server/routes/files/images.js index 8072612a69..c8ebe89473 100644 --- a/api/server/routes/files/images.js +++ b/api/server/routes/files/images.js @@ -2,12 +2,15 @@ const path = require('path'); const fs = require('fs').promises; const express = require('express'); const { logger } = require('@librechat/data-schemas'); +const { verifyAgentUploadPermission } = require('@librechat/api'); const { isAssistantsEndpoint } = require('librechat-data-provider'); const { processAgentFileUpload, processImageFile, filterFile, } = require('~/server/services/Files/process'); +const { checkPermission } = require('~/server/services/PermissionService'); +const db = require('~/models'); const router = express.Router(); @@ -22,6 +25,16 @@ router.post('/', async (req, res) => { metadata.file_id = req.file_id; if (!isAssistantsEndpoint(metadata.endpoint) && metadata.tool_resource != null) { + const denied = await verifyAgentUploadPermission({ + req, + res, + metadata, + getAgent: db.getAgent, + checkPermission, + }); + if (denied) { + return; + } return await processAgentFileUpload({ req, res, metadata }); } diff --git a/api/server/routes/mcp.js b/api/server/routes/mcp.js index 2db8c2c462..d6d7ed5ea0 100644 --- a/api/server/routes/mcp.js +++ b/api/server/routes/mcp.js @@ -13,6 +13,7 @@ const { MCPOAuthHandler, MCPTokenStorage, setOAuthSession, + PENDING_STALE_MS, getUserMCPAuthMap, validateOAuthCsrf, OAUTH_CSRF_COOKIE, @@ -37,18 +38,28 @@ const { } = require('~/config'); const { getMCPSetupData, getServerConnectionStatus } = require('~/server/services/MCP'); const { requireJwtAuth, canAccessMCPServerResource } = require('~/server/middleware'); -const { findToken, updateToken, createToken, deleteTokens } = require('~/models'); const { getUserPluginAuthValue } = require('~/server/services/PluginService'); const { updateMCPServerTools } = require('~/server/services/Config/mcp'); const { reinitMCPServer } = require('~/server/services/Tools/mcp'); -const { findPluginAuthsByKeys } = require('~/models'); -const { getRoleByName } = require('~/models/Role'); const { getLogStores } = require('~/cache'); +const db = require('~/models'); const router = Router(); const OAUTH_CSRF_COOKIE_PATH = '/api/mcp'; +const checkMCPUsePermissions = generateCheckAccess({ + permissionType: PermissionTypes.MCP_SERVERS, + permissions: [Permissions.USE], + getRoleByName: db.getRoleByName, +}); + +const checkMCPCreate = generateCheckAccess({ + permissionType: PermissionTypes.MCP_SERVERS, + permissions: [Permissions.USE, Permissions.CREATE], + getRoleByName: db.getRoleByName, +}); + /** * Get all MCP tools available to the user * Returns only MCP tools, completely decoupled from regular LibreChat tools @@ -91,7 +102,11 @@ router.get('/:serverName/oauth/initiate', requireJwtAuth, setOAuthSession, async } const oauthHeaders = await getOAuthHeaders(serverName, userId); - const { authorizationUrl, flowId: oauthFlowId } = await MCPOAuthHandler.initiateOAuthFlow( + const { + authorizationUrl, + flowId: oauthFlowId, + flowMetadata, + } = await MCPOAuthHandler.initiateOAuthFlow( serverName, serverUrl, userId, @@ -101,6 +116,7 @@ router.get('/:serverName/oauth/initiate', requireJwtAuth, setOAuthSession, async logger.debug('[MCP OAuth] OAuth flow initiated', { oauthFlowId, authorizationUrl }); + await MCPOAuthHandler.storeStateMapping(flowMetadata.state, oauthFlowId, flowManager); setOAuthCsrfCookie(res, oauthFlowId, OAUTH_CSRF_COOKIE_PATH); res.redirect(authorizationUrl); } catch (error) { @@ -143,30 +159,52 @@ router.get('/:serverName/oauth/callback', async (req, res) => { return res.redirect(`${basePath}/oauth/error?error=missing_state`); } - const flowId = state; - logger.debug('[MCP OAuth] Using flow ID from state', { flowId }); + const flowsCache = getLogStores(CacheKeys.FLOWS); + const flowManager = getFlowStateManager(flowsCache); + + const flowId = await MCPOAuthHandler.resolveStateToFlowId(state, flowManager); + if (!flowId) { + logger.error('[MCP OAuth] Could not resolve state to flow ID', { state }); + return res.redirect(`${basePath}/oauth/error?error=invalid_state`); + } + logger.debug('[MCP OAuth] Resolved flow ID from state', { flowId }); const flowParts = flowId.split(':'); if (flowParts.length < 2 || !flowParts[0] || !flowParts[1]) { - logger.error('[MCP OAuth] Invalid flow ID format in state', { flowId }); + logger.error('[MCP OAuth] Invalid flow ID format', { flowId }); return res.redirect(`${basePath}/oauth/error?error=invalid_state`); } const [flowUserId] = flowParts; - if ( - !validateOAuthCsrf(req, res, flowId, OAUTH_CSRF_COOKIE_PATH) && - !validateOAuthSession(req, flowUserId) - ) { - logger.error('[MCP OAuth] CSRF validation failed: no valid CSRF or session cookie', { - flowId, - hasCsrfCookie: !!req.cookies?.[OAUTH_CSRF_COOKIE], - hasSessionCookie: !!req.cookies?.[OAUTH_SESSION_COOKIE], - }); - return res.redirect(`${basePath}/oauth/error?error=csrf_validation_failed`); + + const hasCsrf = validateOAuthCsrf(req, res, flowId, OAUTH_CSRF_COOKIE_PATH); + const hasSession = !hasCsrf && validateOAuthSession(req, flowUserId); + let hasActiveFlow = false; + if (!hasCsrf && !hasSession) { + const pendingFlow = await flowManager.getFlowState(flowId, 'mcp_oauth'); + const pendingAge = pendingFlow?.createdAt ? Date.now() - pendingFlow.createdAt : Infinity; + hasActiveFlow = pendingFlow?.status === 'PENDING' && pendingAge < PENDING_STALE_MS; + if (hasActiveFlow) { + logger.debug( + '[MCP OAuth] CSRF/session cookies absent, validating via active PENDING flow', + { + flowId, + }, + ); + } } - const flowsCache = getLogStores(CacheKeys.FLOWS); - const flowManager = getFlowStateManager(flowsCache); + if (!hasCsrf && !hasSession && !hasActiveFlow) { + logger.error( + '[MCP OAuth] CSRF validation failed: no valid CSRF cookie, session cookie, or active flow', + { + flowId, + hasCsrfCookie: !!req.cookies?.[OAUTH_CSRF_COOKIE], + hasSessionCookie: !!req.cookies?.[OAUTH_SESSION_COOKIE], + }, + ); + return res.redirect(`${basePath}/oauth/error?error=csrf_validation_failed`); + } logger.debug('[MCP OAuth] Getting flow state for flowId: ' + flowId); const flowState = await MCPOAuthHandler.getFlowState(flowId, flowManager); @@ -206,9 +244,9 @@ router.get('/:serverName/oauth/callback', async (req, res) => { userId: flowState.userId, serverName, tokens, - createToken, - updateToken, - findToken, + createToken: db.createToken, + updateToken: db.updateToken, + findToken: db.findToken, clientInfo: flowState.clientInfo, metadata: flowState.metadata, }); @@ -246,10 +284,10 @@ router.get('/:serverName/oauth/callback', async (req, res) => { serverName, flowManager, tokenMethods: { - findToken, - updateToken, - createToken, - deleteTokens, + findToken: db.findToken, + updateToken: db.updateToken, + createToken: db.createToken, + deleteTokens: db.deleteTokens, }, }); @@ -281,7 +319,13 @@ router.get('/:serverName/oauth/callback', async (req, res) => { const toolFlowId = flowState.metadata?.toolFlowId; if (toolFlowId) { logger.debug('[MCP OAuth] Completing tool flow', { toolFlowId }); - await flowManager.completeFlow(toolFlowId, 'mcp_oauth', tokens); + const completed = await flowManager.completeFlow(toolFlowId, 'mcp_oauth', tokens); + if (!completed) { + logger.warn( + '[MCP OAuth] Tool flow state not found during completion — waiter will time out', + { toolFlowId }, + ); + } } /** Redirect to success page with flowId and serverName */ @@ -436,69 +480,75 @@ router.post('/oauth/cancel/:serverName', requireJwtAuth, async (req, res) => { * Reinitialize MCP server * This endpoint allows reinitializing a specific MCP server */ -router.post('/:serverName/reinitialize', requireJwtAuth, setOAuthSession, async (req, res) => { - try { - const { serverName } = req.params; - const user = createSafeUser(req.user); +router.post( + '/:serverName/reinitialize', + requireJwtAuth, + checkMCPUsePermissions, + setOAuthSession, + async (req, res) => { + try { + const { serverName } = req.params; + const user = createSafeUser(req.user); - if (!user.id) { - return res.status(401).json({ error: 'User not authenticated' }); - } + if (!user.id) { + return res.status(401).json({ error: 'User not authenticated' }); + } - logger.info(`[MCP Reinitialize] Reinitializing server: ${serverName}`); + logger.info(`[MCP Reinitialize] Reinitializing server: ${serverName}`); - const mcpManager = getMCPManager(); - const serverConfig = await getMCPServersRegistry().getServerConfig(serverName, user.id); - if (!serverConfig) { - return res.status(404).json({ - error: `MCP server '${serverName}' not found in configuration`, + const mcpManager = getMCPManager(); + const serverConfig = await getMCPServersRegistry().getServerConfig(serverName, user.id); + if (!serverConfig) { + return res.status(404).json({ + error: `MCP server '${serverName}' not found in configuration`, + }); + } + + await mcpManager.disconnectUserConnection(user.id, serverName); + logger.info( + `[MCP Reinitialize] Disconnected existing user connection for server: ${serverName}`, + ); + + /** @type {Record> | undefined} */ + let userMCPAuthMap; + if (serverConfig.customUserVars && typeof serverConfig.customUserVars === 'object') { + userMCPAuthMap = await getUserMCPAuthMap({ + userId: user.id, + servers: [serverName], + findPluginAuthsByKeys: db.findPluginAuthsByKeys, + }); + } + + const result = await reinitMCPServer({ + user, + serverName, + userMCPAuthMap, }); - } - await mcpManager.disconnectUserConnection(user.id, serverName); - logger.info( - `[MCP Reinitialize] Disconnected existing user connection for server: ${serverName}`, - ); + if (!result) { + return res.status(500).json({ error: 'Failed to reinitialize MCP server for user' }); + } - /** @type {Record> | undefined} */ - let userMCPAuthMap; - if (serverConfig.customUserVars && typeof serverConfig.customUserVars === 'object') { - userMCPAuthMap = await getUserMCPAuthMap({ - userId: user.id, - servers: [serverName], - findPluginAuthsByKeys, + const { success, message, oauthRequired, oauthUrl } = result; + + if (oauthRequired) { + const flowId = MCPOAuthHandler.generateFlowId(user.id, serverName); + setOAuthCsrfCookie(res, flowId, OAUTH_CSRF_COOKIE_PATH); + } + + res.json({ + success, + message, + oauthUrl, + serverName, + oauthRequired, }); + } catch (error) { + logger.error('[MCP Reinitialize] Unexpected error', error); + res.status(500).json({ error: 'Internal server error' }); } - - const result = await reinitMCPServer({ - user, - serverName, - userMCPAuthMap, - }); - - if (!result) { - return res.status(500).json({ error: 'Failed to reinitialize MCP server for user' }); - } - - const { success, message, oauthRequired, oauthUrl } = result; - - if (oauthRequired) { - const flowId = MCPOAuthHandler.generateFlowId(user.id, serverName); - setOAuthCsrfCookie(res, flowId, OAUTH_CSRF_COOKIE_PATH); - } - - res.json({ - success, - message, - oauthUrl, - serverName, - oauthRequired, - }); - } catch (error) { - logger.error('[MCP Reinitialize] Unexpected error', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); + }, +); /** * Get connection status for all MCP servers @@ -605,7 +655,7 @@ router.get('/connection/status/:serverName', requireJwtAuth, async (req, res) => * Check which authentication values exist for a specific MCP server * This endpoint returns only boolean flags indicating if values are set, not the actual values */ -router.get('/:serverName/auth-values', requireJwtAuth, async (req, res) => { +router.get('/:serverName/auth-values', requireJwtAuth, checkMCPUsePermissions, async (req, res) => { try { const { serverName } = req.params; const user = req.user; @@ -662,19 +712,6 @@ async function getOAuthHeaders(serverName, userId) { MCP Server CRUD Routes (User-Managed MCP Servers) */ -// Permission checkers for MCP server management -const checkMCPUsePermissions = generateCheckAccess({ - permissionType: PermissionTypes.MCP_SERVERS, - permissions: [Permissions.USE], - getRoleByName, -}); - -const checkMCPCreate = generateCheckAccess({ - permissionType: PermissionTypes.MCP_SERVERS, - permissions: [Permissions.USE, Permissions.CREATE], - getRoleByName, -}); - /** * Get list of accessible MCP servers * @route GET /api/mcp/servers diff --git a/api/server/routes/memories.js b/api/server/routes/memories.js index 58955d8ec4..e71e94f457 100644 --- a/api/server/routes/memories.js +++ b/api/server/routes/memories.js @@ -4,12 +4,12 @@ const { PermissionTypes, Permissions } = require('librechat-data-provider'); const { getAllUserMemories, toggleUserMemories, + getRoleByName, createMemory, deleteMemory, setMemory, } = require('~/models'); const { requireJwtAuth, configMiddleware } = require('~/server/middleware'); -const { getRoleByName } = require('~/models/Role'); const router = express.Router(); diff --git a/api/server/routes/messages.js b/api/server/routes/messages.js index c208e9c406..21b2b23fea 100644 --- a/api/server/routes/messages.js +++ b/api/server/routes/messages.js @@ -3,18 +3,9 @@ const { v4: uuidv4 } = require('uuid'); const { logger } = require('@librechat/data-schemas'); const { ContentTypes } = require('librechat-data-provider'); const { unescapeLaTeX, countTokens } = require('@librechat/api'); -const { - saveConvo, - getMessage, - saveMessage, - getMessages, - updateMessage, - deleteMessages, -} = require('~/models'); const { findAllArtifacts, replaceArtifactContent } = require('~/server/services/Artifacts/update'); const { requireJwtAuth, validateMessageReq } = require('~/server/middleware'); -const { getConvosQueried } = require('~/models/Conversation'); -const { Message } = require('~/db/models'); +const db = require('~/models'); const router = express.Router(); router.use(requireJwtAuth); @@ -40,34 +31,19 @@ router.get('/', async (req, res) => { const sortOrder = sortDirection === 'asc' ? 1 : -1; if (conversationId && messageId) { - const message = await Message.findOne({ - conversationId, - messageId, - user: user, - }).lean(); - response = { messages: message ? [message] : [], nextCursor: null }; + const messages = await db.getMessages({ conversationId, messageId, user }); + response = { messages: messages?.length ? [messages[0]] : [], nextCursor: null }; } else if (conversationId) { - const filter = { conversationId, user: user }; - if (cursor) { - filter[sortField] = sortOrder === 1 ? { $gt: cursor } : { $lt: cursor }; - } - const messages = await Message.find(filter) - .sort({ [sortField]: sortOrder }) - .limit(pageSize + 1) - .lean(); - let nextCursor = null; - if (messages.length > pageSize) { - messages.pop(); // Remove extra item used to detect next page - // Create cursor from the last RETURNED item (not the popped one) - nextCursor = messages[messages.length - 1][sortField]; - } - response = { messages, nextCursor }; + response = await db.getMessagesByCursor( + { conversationId, user }, + { sortField, sortOrder, limit: pageSize, cursor }, + ); } else if (search) { - const searchResults = await Message.meiliSearch(search, { filter: `user = "${user}"` }, true); + const searchResults = await db.searchMessages(search, { filter: `user = "${user}"` }, true); const messages = searchResults.hits || []; - const result = await getConvosQueried(req.user.id, messages, cursor); + const result = await db.getConvosQueried(req.user.id, messages, cursor); const messageIds = []; const cleanedMessages = []; @@ -79,7 +55,7 @@ router.get('/', async (req, res) => { } } - const dbMessages = await getMessages({ + const dbMessages = await db.getMessages({ user, messageId: { $in: messageIds }, }); @@ -136,7 +112,7 @@ router.post('/branch', async (req, res) => { return res.status(400).json({ error: 'messageId and agentId are required' }); } - const sourceMessage = await getMessage({ user: userId, messageId }); + const sourceMessage = await db.getMessage({ user: userId, messageId }); if (!sourceMessage) { return res.status(404).json({ error: 'Source message not found' }); } @@ -187,9 +163,15 @@ router.post('/branch', async (req, res) => { user: userId, }; - const savedMessage = await saveMessage(req, newMessage, { - context: 'POST /api/messages/branch', - }); + const savedMessage = await db.saveMessage( + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, + newMessage, + { context: 'POST /api/messages/branch' }, + ); if (!savedMessage) { return res.status(500).json({ error: 'Failed to save branch message' }); @@ -211,7 +193,7 @@ router.post('/artifact/:messageId', async (req, res) => { return res.status(400).json({ error: 'Invalid request parameters' }); } - const message = await getMessage({ user: req.user.id, messageId }); + const message = await db.getMessage({ user: req.user.id, messageId }); if (!message) { return res.status(404).json({ error: 'Message not found' }); } @@ -256,8 +238,12 @@ router.post('/artifact/:messageId', async (req, res) => { return res.status(400).json({ error: 'Original content not found in target artifact' }); } - const savedMessage = await saveMessage( - req, + const savedMessage = await db.saveMessage( + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { messageId, conversationId: message.conversationId, @@ -283,7 +269,7 @@ router.post('/artifact/:messageId', async (req, res) => { router.get('/:conversationId', validateMessageReq, async (req, res) => { try { const { conversationId } = req.params; - const messages = await getMessages({ conversationId }, '-_id -__v -user'); + const messages = await db.getMessages({ conversationId }, '-_id -__v -user'); res.status(200).json(messages); } catch (error) { logger.error('Error fetching messages:', error); @@ -294,15 +280,20 @@ router.get('/:conversationId', validateMessageReq, async (req, res) => { router.post('/:conversationId', validateMessageReq, async (req, res) => { try { const message = req.body; - const savedMessage = await saveMessage( - req, + const reqCtx = { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }; + const savedMessage = await db.saveMessage( + reqCtx, { ...message, user: req.user.id }, { context: 'POST /api/messages/:conversationId' }, ); if (!savedMessage) { return res.status(400).json({ error: 'Message not saved' }); } - await saveConvo(req, savedMessage, { context: 'POST /api/messages/:conversationId' }); + await db.saveConvo(reqCtx, savedMessage, { context: 'POST /api/messages/:conversationId' }); res.status(201).json(savedMessage); } catch (error) { logger.error('Error saving message:', error); @@ -313,7 +304,7 @@ router.post('/:conversationId', validateMessageReq, async (req, res) => { router.get('/:conversationId/:messageId', validateMessageReq, async (req, res) => { try { const { conversationId, messageId } = req.params; - const message = await getMessages({ conversationId, messageId }, '-_id -__v -user'); + const message = await db.getMessages({ conversationId, messageId }, '-_id -__v -user'); if (!message) { return res.status(404).json({ error: 'Message not found' }); } @@ -331,7 +322,7 @@ router.put('/:conversationId/:messageId', validateMessageReq, async (req, res) = if (index === undefined) { const tokenCount = await countTokens(text, model); - const result = await updateMessage(req, { messageId, text, tokenCount }); + const result = await db.updateMessage(req?.user?.id, { messageId, text, tokenCount }); return res.status(200).json(result); } @@ -339,7 +330,9 @@ router.put('/:conversationId/:messageId', validateMessageReq, async (req, res) = return res.status(400).json({ error: 'Invalid index' }); } - const message = (await getMessages({ conversationId, messageId }, 'content tokenCount'))?.[0]; + const message = ( + await db.getMessages({ conversationId, messageId }, 'content tokenCount') + )?.[0]; if (!message) { return res.status(404).json({ error: 'Message not found' }); } @@ -369,7 +362,11 @@ router.put('/:conversationId/:messageId', validateMessageReq, async (req, res) = tokenCount = Math.max(0, tokenCount - oldTokenCount) + newTokenCount; } - const result = await updateMessage(req, { messageId, content: updatedContent, tokenCount }); + const result = await db.updateMessage(req?.user?.id, { + messageId, + content: updatedContent, + tokenCount, + }); return res.status(200).json(result); } catch (error) { logger.error('Error updating message:', error); @@ -382,8 +379,8 @@ router.put('/:conversationId/:messageId/feedback', validateMessageReq, async (re const { conversationId, messageId } = req.params; const { feedback } = req.body; - const updatedMessage = await updateMessage( - req, + const updatedMessage = await db.updateMessage( + req?.user?.id, { messageId, feedback: feedback || null, @@ -404,8 +401,8 @@ router.put('/:conversationId/:messageId/feedback', validateMessageReq, async (re router.delete('/:conversationId/:messageId', validateMessageReq, async (req, res) => { try { - const { messageId } = req.params; - await deleteMessages({ messageId }); + const { conversationId, messageId } = req.params; + await db.deleteMessages({ messageId, conversationId, user: req.user.id }); res.status(204).send(); } catch (error) { logger.error('Error deleting message:', error); diff --git a/api/server/routes/oauth.js b/api/server/routes/oauth.js index f4bb5b6026..5302158031 100644 --- a/api/server/routes/oauth.js +++ b/api/server/routes/oauth.js @@ -7,12 +7,13 @@ const { ErrorTypes } = require('librechat-data-provider'); const { createSetBalanceConfig } = require('@librechat/api'); const { checkDomainAllowed, loginLimiter, logHeaders } = require('~/server/middleware'); const { createOAuthHandler } = require('~/server/controllers/auth/oauth'); +const { findBalanceByUser, upsertBalanceFields } = require('~/models'); const { getAppConfig } = require('~/server/services/Config'); -const { Balance } = require('~/db/models'); const setBalanceConfig = createSetBalanceConfig({ getAppConfig, - Balance, + findBalanceByUser, + upsertBalanceFields, }); const router = express.Router(); diff --git a/api/server/routes/prompts.js b/api/server/routes/prompts.js index 037bf04813..c2e15ac6c0 100644 --- a/api/server/routes/prompts.js +++ b/api/server/routes/prompts.js @@ -11,13 +11,13 @@ const { } = require('@librechat/api'); const { Permissions, - SystemRoles, ResourceType, AccessRoleIds, PrincipalType, PermissionBits, PermissionTypes, } = require('librechat-data-provider'); +const { SystemCapabilities } = require('@librechat/data-schemas'); const { getListPromptGroupsByAccess, makePromptProduction, @@ -25,12 +25,14 @@ const { deletePromptGroup, createPromptGroup, getPromptGroup, + getRoleByName, deletePrompt, getPrompts, savePrompt, getPrompt, -} = require('~/models/Prompt'); +} = require('~/models'); const { + hasCapability, canAccessPromptGroupResource, canAccessPromptViaGroup, requireJwtAuth, @@ -41,7 +43,6 @@ const { findAccessibleResources, grantPermission, } = require('~/server/services/PermissionService'); -const { getRoleByName } = require('~/models/Role'); const router = express.Router(); @@ -56,15 +57,6 @@ const checkPromptCreate = generateCheckAccess({ getRoleByName, }); -const checkGlobalPromptShare = generateCheckAccess({ - permissionType: PermissionTypes.PROMPTS, - permissions: [Permissions.USE, Permissions.CREATE], - bodyProps: { - [Permissions.SHARE]: ['projectIds', 'removeProjectIds'], - }, - getRoleByName, -}); - router.use(requireJwtAuth); router.use(checkPromptAccess); @@ -342,7 +334,14 @@ const patchPromptGroup = async (req, res) => { const { groupId } = req.params; const author = req.user.id; const filter = { _id: groupId, author }; - if (req.user.role === SystemRoles.ADMIN) { + let canManagePrompts = false; + try { + canManagePrompts = await hasCapability(req.user, SystemCapabilities.MANAGE_PROMPTS); + } catch (err) { + logger.warn(`[patchPromptGroup] capability check failed, denying bypass: ${err.message}`); + } + if (canManagePrompts) { + logger.debug(`[patchPromptGroup] MANAGE_PROMPTS bypass for user ${req.user.id}`); delete filter.author; } @@ -364,7 +363,7 @@ const patchPromptGroup = async (req, res) => { router.patch( '/groups/:groupId', - checkGlobalPromptShare, + checkPromptCreate, canAccessPromptGroupResource({ requiredPermission: PermissionBits.EDIT, }), @@ -430,7 +429,14 @@ router.get('/', async (req, res) => { // If no groupId, return user's own prompts const query = { author }; - if (req.user.role === SystemRoles.ADMIN) { + let canReadPrompts = false; + try { + canReadPrompts = await hasCapability(req.user, SystemCapabilities.READ_PROMPTS); + } catch (err) { + logger.warn(`[GET /prompts] capability check failed, denying bypass: ${err.message}`); + } + if (canReadPrompts) { + logger.debug(`[GET /prompts] READ_PROMPTS bypass for user ${req.user.id}`); delete query.author; } const prompts = await getPrompts(query); @@ -454,8 +460,7 @@ const deletePromptController = async (req, res) => { try { const { promptId } = req.params; const { groupId } = req.query; - const author = req.user.id; - const query = { promptId, groupId, author, role: req.user.role }; + const query = { promptId, groupId }; const result = await deletePrompt(query); res.status(200).send(result); } catch (error) { @@ -473,8 +478,8 @@ const deletePromptController = async (req, res) => { const deletePromptGroupController = async (req, res) => { try { const { groupId: _id } = req.params; - // Don't pass author - permissions are now checked by middleware - const message = await deletePromptGroup({ _id, role: req.user.role }); + // Don't pass author or role - permissions are checked by ACL middleware + const message = await deletePromptGroup({ _id }); res.send(message); } catch (error) { logger.error('Error deleting prompt group', error); diff --git a/api/server/routes/prompts.test.js b/api/server/routes/prompts.test.js index caeb90ddfb..ec162ac1fb 100644 --- a/api/server/routes/prompts.test.js +++ b/api/server/routes/prompts.test.js @@ -10,18 +10,33 @@ const { PrincipalType, PermissionBits, } = require('librechat-data-provider'); +const { SystemCapabilities } = require('@librechat/data-schemas'); // Mock modules before importing jest.mock('~/server/services/Config', () => ({ getCachedTools: jest.fn().mockResolvedValue({}), })); -jest.mock('~/models/Role', () => ({ - getRoleByName: jest.fn(), -})); +jest.mock('~/models', () => { + const mongoose = require('mongoose'); + const { createMethods } = require('@librechat/data-schemas'); + const methods = createMethods(mongoose, { + removeAllPermissions: async ({ resourceType, resourceId }) => { + const AclEntry = mongoose.models.AclEntry; + if (AclEntry) { + await AclEntry.deleteMany({ resourceType, resourceId }); + } + }, + }); + return { + ...methods, + getRoleByName: jest.fn(), + }; +}); jest.mock('~/server/middleware', () => ({ requireJwtAuth: (req, res, next) => next(), + hasCapability: jest.requireActual('~/server/middleware').hasCapability, canAccessPromptViaGroup: jest.requireActual('~/server/middleware').canAccessPromptViaGroup, canAccessPromptGroupResource: jest.requireActual('~/server/middleware').canAccessPromptGroupResource, @@ -30,7 +45,7 @@ jest.mock('~/server/middleware', () => ({ let app; let mongoServer; let promptRoutes; -let Prompt, PromptGroup, AclEntry, AccessRole, User; +let Prompt, PromptGroup, AclEntry, AccessRole, User, SystemGrant; let testUsers, testRoles; let grantPermission; let currentTestUser; // Track current user for middleware @@ -52,6 +67,7 @@ beforeAll(async () => { AclEntry = dbModels.AclEntry; AccessRole = dbModels.AccessRole; User = dbModels.User; + SystemGrant = dbModels.SystemGrant; // Import permission service const permissionService = require('~/server/services/PermissionService'); @@ -152,8 +168,24 @@ async function setupTestData() { }), }; + // Seed capabilities for the ADMIN role + await SystemGrant.create([ + { + principalType: PrincipalType.ROLE, + principalId: SystemRoles.ADMIN, + capability: SystemCapabilities.MANAGE_PROMPTS, + grantedAt: new Date(), + }, + { + principalType: PrincipalType.ROLE, + principalId: SystemRoles.ADMIN, + capability: SystemCapabilities.READ_PROMPTS, + grantedAt: new Date(), + }, + ]); + // Mock getRoleByName - const { getRoleByName } = require('~/models/Role'); + const { getRoleByName } = require('~/models'); getRoleByName.mockImplementation((roleName) => { switch (roleName) { case SystemRoles.USER: diff --git a/api/server/routes/roles.js b/api/server/routes/roles.js index 12e18c7624..1b7e4632e3 100644 --- a/api/server/routes/roles.js +++ b/api/server/routes/roles.js @@ -1,4 +1,5 @@ const express = require('express'); +const { logger, SystemCapabilities } = require('@librechat/data-schemas'); const { SystemRoles, roleDefaults, @@ -11,11 +12,12 @@ const { peoplePickerPermissionsSchema, remoteAgentsPermissionsSchema, } = require('librechat-data-provider'); -const { checkAdmin, requireJwtAuth } = require('~/server/middleware'); -const { updateRoleByName, getRoleByName } = require('~/models/Role'); +const { hasCapability, requireCapability, requireJwtAuth } = require('~/server/middleware'); +const { updateRoleByName, getRoleByName } = require('~/models'); const router = express.Router(); router.use(requireJwtAuth); +const manageRoles = requireCapability(SystemCapabilities.MANAGE_ROLES); /** * Permission configuration mapping @@ -111,14 +113,17 @@ router.get('/:roleName', async (req, res) => { // TODO: TEMP, use a better parsing for roleName const roleName = _r.toUpperCase(); - if ( - (req.user.role !== SystemRoles.ADMIN && roleName === SystemRoles.ADMIN) || - (req.user.role !== SystemRoles.ADMIN && !roleDefaults[roleName]) - ) { - return res.status(403).send({ message: 'Unauthorized' }); - } - try { + let hasReadRoles = false; + try { + hasReadRoles = await hasCapability(req.user, SystemCapabilities.READ_ROLES); + } catch (err) { + logger.warn(`[GET /roles/:roleName] capability check failed: ${err.message}`); + } + if (!hasReadRoles && (roleName === SystemRoles.ADMIN || !roleDefaults[roleName])) { + return res.status(403).send({ message: 'Unauthorized' }); + } + const role = await getRoleByName(roleName, '-_id -__v'); if (!role) { return res.status(404).send({ message: 'Role not found' }); @@ -126,7 +131,8 @@ router.get('/:roleName', async (req, res) => { res.status(200).send(role); } catch (error) { - return res.status(500).send({ message: 'Failed to retrieve role', error: error.message }); + logger.error('[GET /roles/:roleName] Error:', error); + return res.status(500).send({ message: 'Failed to retrieve role' }); } }); @@ -134,42 +140,42 @@ router.get('/:roleName', async (req, res) => { * PUT /api/roles/:roleName/prompts * Update prompt permissions for a specific role */ -router.put('/:roleName/prompts', checkAdmin, createPermissionUpdateHandler('prompts')); +router.put('/:roleName/prompts', manageRoles, createPermissionUpdateHandler('prompts')); /** * PUT /api/roles/:roleName/agents * Update agent permissions for a specific role */ -router.put('/:roleName/agents', checkAdmin, createPermissionUpdateHandler('agents')); +router.put('/:roleName/agents', manageRoles, createPermissionUpdateHandler('agents')); /** * PUT /api/roles/:roleName/memories * Update memory permissions for a specific role */ -router.put('/:roleName/memories', checkAdmin, createPermissionUpdateHandler('memories')); +router.put('/:roleName/memories', manageRoles, createPermissionUpdateHandler('memories')); /** * PUT /api/roles/:roleName/people-picker * Update people picker permissions for a specific role */ -router.put('/:roleName/people-picker', checkAdmin, createPermissionUpdateHandler('people-picker')); +router.put('/:roleName/people-picker', manageRoles, createPermissionUpdateHandler('people-picker')); /** * PUT /api/roles/:roleName/mcp-servers * Update MCP servers permissions for a specific role */ -router.put('/:roleName/mcp-servers', checkAdmin, createPermissionUpdateHandler('mcp-servers')); +router.put('/:roleName/mcp-servers', manageRoles, createPermissionUpdateHandler('mcp-servers')); /** * PUT /api/roles/:roleName/marketplace * Update marketplace permissions for a specific role */ -router.put('/:roleName/marketplace', checkAdmin, createPermissionUpdateHandler('marketplace')); +router.put('/:roleName/marketplace', manageRoles, createPermissionUpdateHandler('marketplace')); /** * PUT /api/roles/:roleName/remote-agents * Update remote agents (API) permissions for a specific role */ -router.put('/:roleName/remote-agents', checkAdmin, createPermissionUpdateHandler('remote-agents')); +router.put('/:roleName/remote-agents', manageRoles, createPermissionUpdateHandler('remote-agents')); module.exports = router; diff --git a/api/server/routes/share.js b/api/server/routes/share.js index 6400b8b637..296644afde 100644 --- a/api/server/routes/share.js +++ b/api/server/routes/share.js @@ -19,9 +19,7 @@ const allowSharedLinks = process.env.ALLOW_SHARED_LINKS === undefined || isEnabled(process.env.ALLOW_SHARED_LINKS); if (allowSharedLinks) { - const allowSharedLinksPublic = - process.env.ALLOW_SHARED_LINKS_PUBLIC === undefined || - isEnabled(process.env.ALLOW_SHARED_LINKS_PUBLIC); + const allowSharedLinksPublic = isEnabled(process.env.ALLOW_SHARED_LINKS_PUBLIC); router.get( '/:shareId', allowSharedLinksPublic ? (req, res, next) => next() : requireJwtAuth, diff --git a/api/server/routes/tags.js b/api/server/routes/tags.js index 0a4ee5084c..a1fa1f77bb 100644 --- a/api/server/routes/tags.js +++ b/api/server/routes/tags.js @@ -8,9 +8,9 @@ const { createConversationTag, deleteConversationTag, getConversationTags, -} = require('~/models/ConversationTag'); + getRoleByName, +} = require('~/models'); const { requireJwtAuth } = require('~/server/middleware'); -const { getRoleByName } = require('~/models/Role'); const router = express.Router(); diff --git a/api/server/services/ActionService.js b/api/server/services/ActionService.js index 5e96726a46..c8ed7bebc4 100644 --- a/api/server/services/ActionService.js +++ b/api/server/services/ActionService.js @@ -20,14 +20,20 @@ const { isImageVisionTool, actionDomainSeparator, } = require('librechat-data-provider'); -const { findToken, updateToken, createToken } = require('~/models'); -const { getActions, deleteActions } = require('~/models/Action'); -const { deleteAssistant } = require('~/models/Assistant'); +const { + findToken, + updateToken, + createToken, + getActions, + deleteActions, + deleteAssistant, +} = require('~/models'); const { getFlowStateManager } = require('~/config'); const { getLogStores } = require('~/cache'); const JWT_SECRET = process.env.JWT_SECRET; const toolNameRegex = /^[a-zA-Z0-9_-]+$/; +const protocolRegex = /^https?:\/\//; const replaceSeparatorRegex = new RegExp(actionDomainSeparator, 'g'); /** @@ -48,7 +54,11 @@ const validateAndUpdateTool = async ({ req, tool, assistant_id }) => { actions = await getActions({ assistant_id, user: req.user.id }, true); const matchingActions = actions.filter((action) => { const metadata = action.metadata; - return metadata && metadata.domain === domain; + if (!metadata) { + return false; + } + const strippedMetaDomain = stripProtocol(metadata.domain); + return strippedMetaDomain === domain || metadata.domain === domain; }); const action = matchingActions[0]; if (!action) { @@ -66,10 +76,36 @@ const validateAndUpdateTool = async ({ req, tool, assistant_id }) => { return tool; }; +/** @param {string} domain */ +function stripProtocol(domain) { + const stripped = domain.replace(protocolRegex, ''); + const pathIdx = stripped.indexOf('/'); + return pathIdx === -1 ? stripped : stripped.substring(0, pathIdx); +} + +/** + * Encodes a domain using the legacy scheme (full URL including protocol). + * Used for backward-compatible matching against agents saved before the collision fix. + * @param {string} domain + * @returns {string} + */ +function legacyDomainEncode(domain) { + if (!domain) { + return ''; + } + if (domain.length <= Constants.ENCODED_DOMAIN_LENGTH) { + return domain.replace(/\./g, actionDomainSeparator); + } + const modifiedDomain = Buffer.from(domain).toString('base64'); + return modifiedDomain.substring(0, Constants.ENCODED_DOMAIN_LENGTH); +} + /** * Encodes or decodes a domain name to/from base64, or replacing periods with a custom separator. * * Necessary due to `[a-zA-Z0-9_-]*` Regex Validation, limited to a 64-character maximum. + * Strips protocol prefix before encoding to prevent base64 collisions + * (all `https://` URLs share the same 10-char base64 prefix). * * @param {string} domain - The domain name to encode/decode. * @param {boolean} inverse - False to decode from base64, true to encode to base64. @@ -79,23 +115,27 @@ async function domainParser(domain, inverse = false) { if (!domain) { return; } - const domainsCache = getLogStores(CacheKeys.ENCODED_DOMAINS); - const cachedDomain = await domainsCache.get(domain); - if (inverse && cachedDomain) { - return domain; - } - if (inverse && domain.length <= Constants.ENCODED_DOMAIN_LENGTH) { - return domain.replace(/\./g, actionDomainSeparator); - } + const domainsCache = getLogStores(CacheKeys.ENCODED_DOMAINS); if (inverse) { - const modifiedDomain = Buffer.from(domain).toString('base64'); + const hostname = stripProtocol(domain); + const cachedDomain = await domainsCache.get(hostname); + if (cachedDomain) { + return hostname; + } + + if (hostname.length <= Constants.ENCODED_DOMAIN_LENGTH) { + return hostname.replace(/\./g, actionDomainSeparator); + } + + const modifiedDomain = Buffer.from(hostname).toString('base64'); const key = modifiedDomain.substring(0, Constants.ENCODED_DOMAIN_LENGTH); await domainsCache.set(key, modifiedDomain); return key; } + const cachedDomain = await domainsCache.get(domain); if (!cachedDomain) { return domain.replace(replaceSeparatorRegex, '.'); } @@ -456,6 +496,7 @@ const deleteAssistantActions = async ({ req, assistant_id }) => { module.exports = { deleteAssistantActions, validateAndUpdateTool, + legacyDomainEncode, createActionTool, encryptMetadata, decryptMetadata, diff --git a/api/server/services/ActionService.spec.js b/api/server/services/ActionService.spec.js index c60aef7ad1..42def44b4f 100644 --- a/api/server/services/ActionService.spec.js +++ b/api/server/services/ActionService.spec.js @@ -1,175 +1,539 @@ -const { Constants, actionDomainSeparator } = require('librechat-data-provider'); -const { domainParser } = require('./ActionService'); +const { Constants, actionDelimiter, actionDomainSeparator } = require('librechat-data-provider'); +const { domainParser, legacyDomainEncode, validateAndUpdateTool } = require('./ActionService'); jest.mock('keyv'); -const globalCache = {}; +jest.mock('~/models/Action', () => ({ + getActions: jest.fn(), + deleteActions: jest.fn(), +})); + +const { getActions } = require('~/models/Action'); + +let mockDomainCache = {}; jest.mock('~/cache/getLogStores', () => { - return jest.fn().mockImplementation(() => { - const EventEmitter = require('events'); - const { CacheKeys } = require('librechat-data-provider'); + return jest.fn().mockImplementation(() => ({ + get: async (key) => mockDomainCache[key] ?? null, + set: async (key, value) => { + mockDomainCache[key] = value; + return true; + }, + })); +}); - class KeyvMongo extends EventEmitter { - constructor(url = 'mongodb://127.0.0.1:27017', options) { - super(); - this.ttlSupport = false; - url = url ?? {}; - if (typeof url === 'string') { - url = { url }; - } - if (url.uri) { - url = { url: url.uri, ...url }; - } - this.opts = { - url, - collection: 'keyv', - ...url, - ...options, - }; - } +beforeEach(() => { + mockDomainCache = {}; + getActions.mockReset(); +}); - get = async (key) => { - return new Promise((resolve) => { - resolve(globalCache[key] || null); - }); - }; +const SEP = actionDomainSeparator; +const DELIM = actionDelimiter; +const MAX = Constants.ENCODED_DOMAIN_LENGTH; +const domainSepRegex = new RegExp(SEP, 'g'); - set = async (key, value) => { - return new Promise((resolve) => { - globalCache[key] = value; - resolve(true); - }); - }; - } +describe('domainParser', () => { + describe('nullish input', () => { + it.each([null, undefined, ''])('returns undefined for %j', async (input) => { + expect(await domainParser(input, true)).toBeUndefined(); + expect(await domainParser(input, false)).toBeUndefined(); + }); + }); - return new KeyvMongo('', { - namespace: CacheKeys.ENCODED_DOMAINS, - ttl: 0, + describe('short-path encoding (hostname ≤ threshold)', () => { + it.each([ + ['examp.com', `examp${SEP}com`], + ['swapi.tech', `swapi${SEP}tech`], + ['a.b', `a${SEP}b`], + ])('replaces dots in %s → %s', async (domain, expected) => { + expect(await domainParser(domain, true)).toBe(expected); + }); + + it('handles domain exactly at threshold length', async () => { + const domain = 'a'.repeat(MAX - 4) + '.com'; + expect(domain).toHaveLength(MAX); + const result = await domainParser(domain, true); + expect(result).toBe(domain.replace(/\./g, SEP)); + }); + }); + + describe('base64-path encoding (hostname > threshold)', () => { + it('produces a key of exactly ENCODED_DOMAIN_LENGTH chars', async () => { + const result = await domainParser('api.example.com', true); + expect(result).toHaveLength(MAX); + }); + + it('encodes hostname, not full URL', async () => { + const hostname = 'api.example.com'; + const expectedKey = Buffer.from(hostname).toString('base64').substring(0, MAX); + expect(await domainParser(hostname, true)).toBe(expectedKey); + }); + + it('populates decode cache for round-trip', async () => { + const hostname = 'longdomainname.com'; + const key = await domainParser(hostname, true); + + expect(mockDomainCache[key]).toBe(Buffer.from(hostname).toString('base64')); + expect(await domainParser(key, false)).toBe(hostname); + }); + }); + + describe('protocol stripping', () => { + it('https:// URL and bare hostname produce identical encoding', async () => { + const encoded = await domainParser('https://swapi.tech', true); + expect(encoded).toBe(await domainParser('swapi.tech', true)); + expect(encoded).toBe(`swapi${SEP}tech`); + }); + + it('http:// URL and bare hostname produce identical encoding', async () => { + const encoded = await domainParser('http://api.example.com', true); + expect(encoded).toBe(await domainParser('api.example.com', true)); + }); + + it('different https:// domains produce unique keys', async () => { + const keys = await Promise.all([ + domainParser('https://api.example.com', true), + domainParser('https://api.weather.com', true), + domainParser('https://data.github.com', true), + ]); + const unique = new Set(keys); + expect(unique.size).toBe(keys.length); + }); + + it('long hostname after stripping still uses base64 path', async () => { + const result = await domainParser('https://api.example.com', true); + expect(result).toHaveLength(MAX); + expect(result).not.toContain(SEP); + }); + + it('short hostname after stripping uses dot-replacement path', async () => { + const result = await domainParser('https://a.b.c', true); + expect(result).toBe(`a${SEP}b${SEP}c`); + }); + + it('strips path and query from full URL before encoding', async () => { + const result = await domainParser('https://api.example.com/v1/endpoint?foo=bar', true); + expect(result).toBe(await domainParser('api.example.com', true)); + }); + }); + + describe('unicode domains', () => { + it('encodes unicode hostname via base64 path', async () => { + const domain = 'täst.example.com'; + const result = await domainParser(domain, true); + expect(result).toHaveLength(MAX); + expect(result).toBe(Buffer.from(domain).toString('base64').substring(0, MAX)); + }); + + it('round-trips unicode hostname through encode then decode', async () => { + const domain = 'täst.example.com'; + const key = await domainParser(domain, true); + expect(await domainParser(key, false)).toBe(domain); + }); + + it('strips protocol before encoding unicode hostname', async () => { + const withProto = 'https://täst.example.com'; + const bare = 'täst.example.com'; + expect(await domainParser(withProto, true)).toBe(await domainParser(bare, true)); + }); + }); + + describe('decode path', () => { + it('short-path encoded domain decodes via separator replacement', async () => { + expect(await domainParser(`examp${SEP}com`, false)).toBe('examp.com'); + }); + + it('base64-path encoded domain decodes via cache lookup', async () => { + const hostname = 'api.example.com'; + const key = await domainParser(hostname, true); + expect(await domainParser(key, false)).toBe(hostname); + }); + + it('returns input unchanged for unknown non-separator strings', async () => { + expect(await domainParser('not_base64_encoded', false)).toBe('not_base64_encoded'); + }); + + it('returns a string without throwing for corrupt cache entries', async () => { + mockDomainCache['corrupt_key'] = '!!!'; + const result = await domainParser('corrupt_key', false); + expect(typeof result).toBe('string'); }); }); }); -describe('domainParser', () => { - const TLD = '.com'; - - // Non-azure request - it('does not return domain as is if not azure', async () => { - const domain = `example.com${actionDomainSeparator}test${actionDomainSeparator}`; - const result1 = await domainParser(domain, false); - const result2 = await domainParser(domain, true); - expect(result1).not.toEqual(domain); - expect(result2).not.toEqual(domain); +describe('legacyDomainEncode', () => { + it.each(['', null, undefined])('returns empty string for %j', (input) => { + expect(legacyDomainEncode(input)).toBe(''); }); - // Test for Empty or Null Inputs - it('returns undefined for null domain input', async () => { - const result = await domainParser(null, true); - expect(result).toBeUndefined(); + it('is synchronous (returns a string, not a Promise)', () => { + const result = legacyDomainEncode('examp.com'); + expect(result).toBe(`examp${SEP}com`); + expect(result).not.toBeInstanceOf(Promise); }); - it('returns undefined for empty domain input', async () => { - const result = await domainParser('', true); - expect(result).toBeUndefined(); + it('uses dot-replacement for short domains', () => { + expect(legacyDomainEncode('examp.com')).toBe(`examp${SEP}com`); }); - // Verify Correct Caching Behavior - it('caches encoded domain correctly', async () => { - const domain = 'longdomainname.com'; - const encodedDomain = Buffer.from(domain) - .toString('base64') - .substring(0, Constants.ENCODED_DOMAIN_LENGTH); - - await domainParser(domain, true); - - const cachedValue = await globalCache[encodedDomain]; - expect(cachedValue).toEqual(Buffer.from(domain).toString('base64')); + it('uses base64 prefix of full input for long domains', () => { + const domain = 'https://swapi.tech'; + const expected = Buffer.from(domain).toString('base64').substring(0, MAX); + expect(legacyDomainEncode(domain)).toBe(expected); }); - // Test for Edge Cases Around Length Threshold - it('encodes domain exactly at threshold without modification', async () => { - const domain = 'a'.repeat(Constants.ENCODED_DOMAIN_LENGTH - TLD.length) + TLD; - const expected = domain.replace(/\./g, actionDomainSeparator); - const result = await domainParser(domain, true); - expect(result).toEqual(expected); + it('all https:// URLs collide to the same key', () => { + const results = [ + legacyDomainEncode('https://api.example.com'), + legacyDomainEncode('https://api.weather.com'), + legacyDomainEncode('https://totally.different.host'), + ]; + expect(new Set(results).size).toBe(1); }); - it('encodes domain just below threshold without modification', async () => { - const domain = 'a'.repeat(Constants.ENCODED_DOMAIN_LENGTH - 1 - TLD.length) + TLD; - const expected = domain.replace(/\./g, actionDomainSeparator); - const result = await domainParser(domain, true); - expect(result).toEqual(expected); + it('matches what old domainParser would have produced', () => { + const domain = 'https://api.example.com'; + const legacy = legacyDomainEncode(domain); + expect(legacy).toBe(Buffer.from(domain).toString('base64').substring(0, MAX)); }); - // Test for Unicode Domain Names - it('handles unicode characters in domain names correctly when encoding', async () => { - const unicodeDomain = 'täst.example.com'; - const encodedDomain = Buffer.from(unicodeDomain) - .toString('base64') - .substring(0, Constants.ENCODED_DOMAIN_LENGTH); - const result = await domainParser(unicodeDomain, true); - expect(result).toEqual(encodedDomain); - }); - - it('decodes unicode domain names correctly', async () => { - const unicodeDomain = 'täst.example.com'; - const encodedDomain = Buffer.from(unicodeDomain).toString('base64'); - globalCache[encodedDomain.substring(0, Constants.ENCODED_DOMAIN_LENGTH)] = encodedDomain; // Simulate caching - - const result = await domainParser( - encodedDomain.substring(0, Constants.ENCODED_DOMAIN_LENGTH), - false, - ); - expect(result).toEqual(unicodeDomain); - }); - - // Core Functionality Tests - it('returns domain with replaced separators if no cached domain exists', async () => { - const domain = 'example.com'; - const withSeparator = domain.replace(/\./g, actionDomainSeparator); - const result = await domainParser(withSeparator, false); - expect(result).toEqual(domain); - }); - - it('returns domain with replaced separators when inverse is false and under encoding length', async () => { - const domain = 'examp.com'; - const withSeparator = domain.replace(/\./g, actionDomainSeparator); - const result = await domainParser(withSeparator, false); - expect(result).toEqual(domain); - }); - - it('replaces periods with actionDomainSeparator when inverse is true and under encoding length', async () => { - const domain = 'examp.com'; - const expected = domain.replace(/\./g, actionDomainSeparator); - const result = await domainParser(domain, true); - expect(result).toEqual(expected); - }); - - it('encodes domain when length is above threshold and inverse is true', async () => { - const domain = 'a'.repeat(Constants.ENCODED_DOMAIN_LENGTH + 1).concat('.com'); - const result = await domainParser(domain, true); - expect(result).not.toEqual(domain); - expect(result.length).toBeLessThanOrEqual(Constants.ENCODED_DOMAIN_LENGTH); - }); - - it('returns encoded value if no encoded value is cached, and inverse is false', async () => { - const originalDomain = 'example.com'; - const encodedDomain = Buffer.from( - originalDomain.replace(/\./g, actionDomainSeparator), - ).toString('base64'); - const result = await domainParser(encodedDomain, false); - expect(result).toEqual(encodedDomain); - }); - - it('decodes encoded value if cached and encoded value is provided, and inverse is false', async () => { - const originalDomain = 'example.com'; - const encodedDomain = await domainParser(originalDomain, true); - const result = await domainParser(encodedDomain, false); - expect(result).toEqual(originalDomain); - }); - - it('handles invalid base64 encoded values gracefully', async () => { - const invalidBase64Domain = 'not_base64_encoded'; - const result = await domainParser(invalidBase64Domain, false); - expect(result).toEqual(invalidBase64Domain); + it('produces same result as new domainParser for short bare hostnames', async () => { + const domain = 'swapi.tech'; + expect(legacyDomainEncode(domain)).toBe(await domainParser(domain, true)); + }); +}); + +describe('validateAndUpdateTool', () => { + const mockReq = { user: { id: 'user123' } }; + + it('returns tool unchanged when name passes tool-name regex', async () => { + const tool = { function: { name: 'getPeople_action_swapi---tech' } }; + const result = await validateAndUpdateTool({ + req: mockReq, + tool, + assistant_id: 'asst_1', + }); + expect(result).toEqual(tool); + expect(getActions).not.toHaveBeenCalled(); + }); + + it('matches action when metadata.domain has https:// prefix and tool domain is bare hostname', async () => { + getActions.mockResolvedValue([{ metadata: { domain: 'https://api.example.com' } }]); + + const tool = { function: { name: `getPeople${DELIM}api.example.com` } }; + const result = await validateAndUpdateTool({ + req: mockReq, + tool, + assistant_id: 'asst_1', + }); + + expect(result).not.toBeNull(); + expect(result.function.name).toMatch(/^getPeople_action_/); + expect(result.function.name).not.toContain('.'); + }); + + it('matches action when metadata.domain has no protocol', async () => { + getActions.mockResolvedValue([{ metadata: { domain: 'api.example.com' } }]); + + const tool = { function: { name: `getPeople${DELIM}api.example.com` } }; + const result = await validateAndUpdateTool({ + req: mockReq, + tool, + assistant_id: 'asst_1', + }); + + expect(result).not.toBeNull(); + expect(result.function.name).toMatch(/^getPeople_action_/); + }); + + it('returns null when no action matches the domain', async () => { + getActions.mockResolvedValue([{ metadata: { domain: 'https://other.domain.com' } }]); + + const tool = { function: { name: `getPeople${DELIM}api.example.com` } }; + const result = await validateAndUpdateTool({ + req: mockReq, + tool, + assistant_id: 'asst_1', + }); + + expect(result).toBeNull(); + }); + + it('returns null when action has no metadata', async () => { + getActions.mockResolvedValue([{ metadata: null }]); + + const tool = { function: { name: `getPeople${DELIM}api.example.com` } }; + const result = await validateAndUpdateTool({ + req: mockReq, + tool, + assistant_id: 'asst_1', + }); + + expect(result).toBeNull(); + }); +}); + +describe('backward-compatible tool name matching', () => { + function normalizeToolName(name) { + return name.replace(domainSepRegex, '_'); + } + + function buildToolName(functionName, encodedDomain) { + return `${functionName}${DELIM}${encodedDomain}`; + } + + describe('definition-phase matching', () => { + it('new encoding matches agent tools stored with new encoding', async () => { + const metadataDomain = 'https://swapi.tech'; + const encoded = await domainParser(metadataDomain, true); + const normalized = normalizeToolName(encoded); + + const storedTool = buildToolName('getPeople', encoded); + const defToolName = `getPeople${DELIM}${normalized}`; + + expect(normalizeToolName(storedTool)).toBe(defToolName); + }); + + it('legacy encoding matches agent tools stored with legacy encoding', async () => { + const metadataDomain = 'https://swapi.tech'; + const legacy = legacyDomainEncode(metadataDomain); + const legacyNormalized = normalizeToolName(legacy); + + const storedTool = buildToolName('getPeople', legacy); + const legacyDefName = `getPeople${DELIM}${legacyNormalized}`; + + expect(normalizeToolName(storedTool)).toBe(legacyDefName); + }); + + it('new definition matches old stored tools via legacy fallback', async () => { + const metadataDomain = 'https://swapi.tech'; + const newDomain = await domainParser(metadataDomain, true); + const legacyDomain = legacyDomainEncode(metadataDomain); + const newNorm = normalizeToolName(newDomain); + const legacyNorm = normalizeToolName(legacyDomain); + + const oldStoredTool = buildToolName('getPeople', legacyDomain); + const newToolName = `getPeople${DELIM}${newNorm}`; + const legacyToolName = `getPeople${DELIM}${legacyNorm}`; + + const storedNormalized = normalizeToolName(oldStoredTool); + const hasMatch = storedNormalized === newToolName || storedNormalized === legacyToolName; + expect(hasMatch).toBe(true); + }); + + it('pre-normalized Set eliminates per-tool normalization', async () => { + const metadataDomain = 'https://api.example.com'; + const domain = await domainParser(metadataDomain, true); + const legacyDomain = legacyDomainEncode(metadataDomain); + const normalizedDomain = normalizeToolName(domain); + const legacyNormalized = normalizeToolName(legacyDomain); + + const storedTools = [ + buildToolName('getWeather', legacyDomain), + buildToolName('getForecast', domain), + ]; + + const preNormalized = new Set(storedTools.map((t) => normalizeToolName(t))); + + const toolName = `getWeather${DELIM}${normalizedDomain}`; + const legacyToolName = `getWeather${DELIM}${legacyNormalized}`; + expect(preNormalized.has(toolName) || preNormalized.has(legacyToolName)).toBe(true); + }); + }); + + describe('execution-phase tool lookup', () => { + it('model-called tool name resolves via normalizedToDomain map (new encoding)', async () => { + const metadataDomain = 'https://api.example.com'; + const domain = await domainParser(metadataDomain, true); + const normalized = normalizeToolName(domain); + + const normalizedToDomain = new Map(); + normalizedToDomain.set(normalized, domain); + + const modelToolName = `getWeather${DELIM}${normalized}`; + + let matched = ''; + for (const [norm, canonical] of normalizedToDomain.entries()) { + if (modelToolName.includes(norm)) { + matched = canonical; + break; + } + } + + expect(matched).toBe(domain); + + const functionName = modelToolName.replace(`${DELIM}${normalizeToolName(matched)}`, ''); + expect(functionName).toBe('getWeather'); + }); + + it('model-called tool name resolves via legacy entry in normalizedToDomain map', async () => { + const metadataDomain = 'https://api.example.com'; + const domain = await domainParser(metadataDomain, true); + const legacyDomain = legacyDomainEncode(metadataDomain); + const legacyNorm = normalizeToolName(legacyDomain); + + const normalizedToDomain = new Map(); + normalizedToDomain.set(normalizeToolName(domain), domain); + normalizedToDomain.set(legacyNorm, domain); + + const legacyModelToolName = `getWeather${DELIM}${legacyNorm}`; + + let matched = ''; + for (const [norm, canonical] of normalizedToDomain.entries()) { + if (legacyModelToolName.includes(norm)) { + matched = canonical; + break; + } + } + + expect(matched).toBe(domain); + }); + + it('legacy guard skips duplicate map entry for short bare hostnames', async () => { + const domain = 'swapi.tech'; + const newEncoding = await domainParser(domain, true); + const legacyEncoding = legacyDomainEncode(domain); + + expect(newEncoding).toBe(legacyEncoding); + + const normalizedToDomain = new Map(); + normalizedToDomain.set(newEncoding, newEncoding); + if (legacyEncoding !== newEncoding) { + normalizedToDomain.set(legacyEncoding, newEncoding); + } + expect(normalizedToDomain.size).toBe(1); + }); + }); + + describe('processRequiredActions matching (assistants path)', () => { + it('legacy tool from OpenAI matches via normalizedToDomain with both encodings', async () => { + const metadataDomain = 'https://swapi.tech'; + const domain = await domainParser(metadataDomain, true); + const legacyDomain = legacyDomainEncode(metadataDomain); + + const normalizedToDomain = new Map(); + normalizedToDomain.set(domain, domain); + if (legacyDomain !== domain) { + normalizedToDomain.set(legacyDomain, domain); + } + + const legacyToolName = buildToolName('getPeople', legacyDomain); + + let currentDomain = ''; + let matchedKey = ''; + for (const [key, canonical] of normalizedToDomain.entries()) { + if (legacyToolName.includes(key)) { + currentDomain = canonical; + matchedKey = key; + break; + } + } + + expect(currentDomain).toBe(domain); + expect(matchedKey).toBe(legacyDomain); + + const functionName = legacyToolName.replace(`${DELIM}${matchedKey}`, ''); + expect(functionName).toBe('getPeople'); + }); + + it('new tool name matches via the canonical domain key', async () => { + const metadataDomain = 'https://swapi.tech'; + const domain = await domainParser(metadataDomain, true); + const legacyDomain = legacyDomainEncode(metadataDomain); + + const normalizedToDomain = new Map(); + normalizedToDomain.set(domain, domain); + if (legacyDomain !== domain) { + normalizedToDomain.set(legacyDomain, domain); + } + + const newToolName = buildToolName('getPeople', domain); + + let currentDomain = ''; + let matchedKey = ''; + for (const [key, canonical] of normalizedToDomain.entries()) { + if (newToolName.includes(key)) { + currentDomain = canonical; + matchedKey = key; + break; + } + } + + expect(currentDomain).toBe(domain); + expect(matchedKey).toBe(domain); + + const functionName = newToolName.replace(`${DELIM}${matchedKey}`, ''); + expect(functionName).toBe('getPeople'); + }); + }); + + describe('save-route cleanup', () => { + it('tool filter removes tools matching new encoding', async () => { + const metadataDomain = 'https://swapi.tech'; + const domain = await domainParser(metadataDomain, true); + const legacyDomain = legacyDomainEncode(metadataDomain); + + const tools = [ + buildToolName('getPeople', domain), + buildToolName('unrelated', 'other---domain'), + ]; + + const filtered = tools.filter((t) => !t.includes(domain) && !t.includes(legacyDomain)); + + expect(filtered).toEqual([buildToolName('unrelated', 'other---domain')]); + }); + + it('tool filter removes tools matching legacy encoding', async () => { + const metadataDomain = 'https://swapi.tech'; + const domain = await domainParser(metadataDomain, true); + const legacyDomain = legacyDomainEncode(metadataDomain); + + const tools = [ + buildToolName('getPeople', legacyDomain), + buildToolName('unrelated', 'other---domain'), + ]; + + const filtered = tools.filter((t) => !t.includes(domain) && !t.includes(legacyDomain)); + + expect(filtered).toEqual([buildToolName('unrelated', 'other---domain')]); + }); + }); + + describe('delete-route domain extraction', () => { + it('domain extracted from actions array is usable as-is for tool filtering', async () => { + const metadataDomain = 'https://api.example.com'; + const domain = await domainParser(metadataDomain, true); + const actionId = 'abc123'; + const actionEntry = `${domain}${DELIM}${actionId}`; + + const [storedDomain] = actionEntry.split(DELIM); + expect(storedDomain).toBe(domain); + + const tools = [buildToolName('getWeather', domain), buildToolName('getPeople', 'other')]; + + const filtered = tools.filter((t) => !t.includes(storedDomain)); + expect(filtered).toEqual([buildToolName('getPeople', 'other')]); + }); + }); + + describe('multi-action agents (collision scenario)', () => { + it('two https:// actions now produce distinct tool names', async () => { + const domain1 = await domainParser('https://api.weather.com', true); + const domain2 = await domainParser('https://api.spacex.com', true); + + const tool1 = buildToolName('getData', domain1); + const tool2 = buildToolName('getData', domain2); + + expect(tool1).not.toBe(tool2); + }); + + it('two https:// actions used to collide in legacy encoding', () => { + const legacy1 = legacyDomainEncode('https://api.weather.com'); + const legacy2 = legacyDomainEncode('https://api.spacex.com'); + + const tool1 = buildToolName('getData', legacy1); + const tool2 = buildToolName('getData', legacy2); + + expect(tool1).toBe(tool2); + }); }); }); diff --git a/api/server/services/AuthService.js b/api/server/services/AuthService.js index 1b4b653c1a..ef50a365b9 100644 --- a/api/server/services/AuthService.js +++ b/api/server/services/AuthService.js @@ -462,6 +462,22 @@ const setOpenIDAuthTokens = (tokenset, req, res, userId, existingRefreshToken) = */ const appAuthToken = tokenset.id_token || tokenset.access_token; + /** + * Always set refresh token cookie so it survives express session expiry. + * The session cookie maxAge (SESSION_EXPIRY, default 15 min) is typically shorter + * than the OIDC token lifetime (~1 hour). Without this cookie fallback, the refresh + * token stored only in the session is lost when the session expires, causing the user + * to be signed out on the next token refresh attempt. + * The refresh token is small (opaque string) so it doesn't hit the HTTP/2 header + * size limits that motivated session storage for the larger access_token/id_token. + */ + res.cookie('refreshToken', refreshToken, { + expires: expirationDate, + httpOnly: true, + secure: shouldUseSecureCookie(), + sameSite: 'strict', + }); + /** Store tokens server-side in session to avoid large cookies */ if (req.session) { req.session.openidTokens = { @@ -472,12 +488,6 @@ const setOpenIDAuthTokens = (tokenset, req, res, userId, existingRefreshToken) = }; } else { logger.warn('[setOpenIDAuthTokens] No session available, falling back to cookies'); - res.cookie('refreshToken', refreshToken, { - expires: expirationDate, - httpOnly: true, - secure: shouldUseSecureCookie(), - sameSite: 'strict', - }); res.cookie('openid_access_token', tokenset.access_token, { expires: expirationDate, httpOnly: true, diff --git a/api/server/services/Endpoints/agents/addedConvo.js b/api/server/services/Endpoints/agents/addedConvo.js index 7e9385267a..7561053f8f 100644 --- a/api/server/services/Endpoints/agents/addedConvo.js +++ b/api/server/services/Endpoints/agents/addedConvo.js @@ -1,12 +1,16 @@ const { logger } = require('@librechat/data-schemas'); -const { initializeAgent, validateAgentModel } = require('@librechat/api'); -const { loadAddedAgent, setGetAgent, ADDED_AGENT_ID } = require('~/models/loadAddedAgent'); -const { getConvoFiles } = require('~/models/Conversation'); -const { getAgent } = require('~/models/Agent'); +const { + ADDED_AGENT_ID, + initializeAgent, + validateAgentModel, + loadAddedAgent: loadAddedAgentFn, +} = require('@librechat/api'); +const { filterFilesByAgentAccess } = require('~/server/services/Files/permissions'); +const { getMCPServerTools } = require('~/server/services/Config'); const db = require('~/models'); -// Initialize the getAgent dependency -setGetAgent(getAgent); +const loadAddedAgent = (params) => + loadAddedAgentFn(params, { getAgent: db.getAgent, getMCPServerTools }); /** * Process addedConvo for parallel agent execution. @@ -55,16 +59,16 @@ const processAddedConvo = async ({ userMCPAuthMap, }) => { const addedConvo = endpointOption.addedConvo; - logger.debug('[processAddedConvo] Called with addedConvo:', { - hasAddedConvo: addedConvo != null, - addedConvoEndpoint: addedConvo?.endpoint, - addedConvoModel: addedConvo?.model, - addedConvoAgentId: addedConvo?.agent_id, - }); if (addedConvo == null) { return { userMCPAuthMap }; } + logger.debug('[processAddedConvo] Processing added conversation', { + model: addedConvo.model, + agentId: addedConvo.agent_id, + endpoint: addedConvo.endpoint, + }); + try { const addedAgent = await loadAddedAgent({ req, conversation: addedConvo, primaryAgent }); if (!addedAgent) { @@ -99,15 +103,16 @@ const processAddedConvo = async ({ allowedProviders, }, { - getConvoFiles, getFiles: db.getFiles, getUserKey: db.getUserKey, getMessages: db.getMessages, + getConvoFiles: db.getConvoFiles, updateFilesUsage: db.updateFilesUsage, getUserCodeFiles: db.getUserCodeFiles, getUserKeyValues: db.getUserKeyValues, getToolFilesByIds: db.getToolFilesByIds, getCodeGeneratedFiles: db.getCodeGeneratedFiles, + filterFilesByAgentAccess, }, ); diff --git a/api/server/services/Endpoints/agents/build.js b/api/server/services/Endpoints/agents/build.js index a95640e528..19ae3ab7e8 100644 --- a/api/server/services/Endpoints/agents/build.js +++ b/api/server/services/Endpoints/agents/build.js @@ -1,6 +1,10 @@ const { logger } = require('@librechat/data-schemas'); +const { loadAgent: loadAgentFn } = require('@librechat/api'); const { isAgentsEndpoint, removeNullishValues, Constants } = require('librechat-data-provider'); -const { loadAgent } = require('~/models/Agent'); +const { getMCPServerTools } = require('~/server/services/Config'); +const db = require('~/models'); + +const loadAgent = (params) => loadAgentFn(params, { getAgent: db.getAgent, getMCPServerTools }); const buildOptions = (req, endpoint, parsedBody, endpointType) => { const { spec, iconURL, agent_id, ...model_parameters } = parsedBody; diff --git a/api/server/services/Endpoints/agents/initialize.js b/api/server/services/Endpoints/agents/initialize.js index 0888f23cd5..28282e68ea 100644 --- a/api/server/services/Endpoints/agents/initialize.js +++ b/api/server/services/Endpoints/agents/initialize.js @@ -10,6 +10,8 @@ const { createSequentialChainEdges, } = require('@librechat/api'); const { + ResourceType, + PermissionBits, EModelEndpoint, isAgentsEndpoint, getResponseSender, @@ -20,11 +22,11 @@ const { getDefaultHandlers, } = require('~/server/controllers/agents/callbacks'); const { loadAgentTools, loadToolsForExecution } = require('~/server/services/ToolService'); +const { filterFilesByAgentAccess } = require('~/server/services/Files/permissions'); const { getModelsConfig } = require('~/server/controllers/ModelController'); +const { checkPermission } = require('~/server/services/PermissionService'); const AgentClient = require('~/server/controllers/agents/client'); -const { getConvoFiles } = require('~/models/Conversation'); const { processAddedConvo } = require('./addedConvo'); -const { getAgent } = require('~/models/Agent'); const { logViolation } = require('~/cache'); const db = require('~/models'); @@ -125,6 +127,7 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { toolRegistry: ctx.toolRegistry, userMCPAuthMap: ctx.userMCPAuthMap, tool_resources: ctx.tool_resources, + actionsEnabled: ctx.actionsEnabled, }); logger.debug(`[ON_TOOL_EXECUTE] loaded ${result.loadedTools?.length ?? 0} tools`); @@ -191,32 +194,28 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { isInitialAgent: true, }, { - getConvoFiles, getFiles: db.getFiles, getUserKey: db.getUserKey, getMessages: db.getMessages, + getConvoFiles: db.getConvoFiles, updateFilesUsage: db.updateFilesUsage, getUserKeyValues: db.getUserKeyValues, getUserCodeFiles: db.getUserCodeFiles, getToolFilesByIds: db.getToolFilesByIds, getCodeGeneratedFiles: db.getCodeGeneratedFiles, + filterFilesByAgentAccess, }, ); logger.debug( - `[initializeClient] Tool definitions for primary agent: ${primaryConfig.toolDefinitions?.length ?? 0}`, - ); - - /** Store primary agent's tool context for ON_TOOL_EXECUTE callback */ - logger.debug(`[initializeClient] Storing tool context for agentId: ${primaryConfig.id}`); - logger.debug( - `[initializeClient] toolRegistry size: ${primaryConfig.toolRegistry?.size ?? 'undefined'}`, + `[initializeClient] Storing tool context for ${primaryConfig.id}: ${primaryConfig.toolDefinitions?.length ?? 0} tools, registry size: ${primaryConfig.toolRegistry?.size ?? '0'}`, ); agentToolContexts.set(primaryConfig.id, { agent: primaryAgent, toolRegistry: primaryConfig.toolRegistry, userMCPAuthMap: primaryConfig.userMCPAuthMap, tool_resources: primaryConfig.tool_resources, + actionsEnabled: primaryConfig.actionsEnabled, }); const agent_ids = primaryConfig.agent_ids; @@ -226,7 +225,7 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { const skippedAgentIds = new Set(); async function processAgent(agentId) { - const agent = await getAgent({ id: agentId }); + const agent = await db.getAgent({ id: agentId }); if (!agent) { logger.warn( `[processAgent] Handoff agent ${agentId} not found, skipping (orphaned reference)`, @@ -235,6 +234,22 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { return null; } + const hasAccess = await checkPermission({ + userId: req.user.id, + role: req.user.role, + resourceType: ResourceType.AGENT, + resourceId: agent._id, + requiredPermission: PermissionBits.VIEW, + }); + + if (!hasAccess) { + logger.warn( + `[processAgent] User ${req.user.id} lacks VIEW access to handoff agent ${agentId}, skipping`, + ); + skippedAgentIds.add(agentId); + return null; + } + const validationResult = await validateAgentModel({ req, res, @@ -260,15 +275,16 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { allowedProviders, }, { - getConvoFiles, getFiles: db.getFiles, getUserKey: db.getUserKey, getMessages: db.getMessages, + getConvoFiles: db.getConvoFiles, updateFilesUsage: db.updateFilesUsage, getUserKeyValues: db.getUserKeyValues, getUserCodeFiles: db.getUserCodeFiles, getToolFilesByIds: db.getToolFilesByIds, getCodeGeneratedFiles: db.getCodeGeneratedFiles, + filterFilesByAgentAccess, }, ); @@ -284,6 +300,7 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { toolRegistry: config.toolRegistry, userMCPAuthMap: config.userMCPAuthMap, tool_resources: config.tool_resources, + actionsEnabled: config.actionsEnabled, }); agentConfigs.set(agentId, config); @@ -312,6 +329,7 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { } } catch (err) { logger.error(`[initializeClient] Error processing agent ${agentId}:`, err); + skippedAgentIds.add(agentId); } } @@ -321,7 +339,12 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { if (checkAgentInit(agentId)) { continue; } - await processAgent(agentId); + try { + await processAgent(agentId); + } catch (err) { + logger.error(`[initializeClient] Error processing chain agent ${agentId}:`, err); + skippedAgentIds.add(agentId); + } } const chain = await createSequentialChainEdges([primaryConfig.id].concat(agent_ids), '{convo}'); collectEdges(chain); @@ -351,6 +374,19 @@ const initializeClient = async ({ req, res, signal, endpointOption }) => { userMCPAuthMap = updatedMCPAuthMap; } + for (const [agentId, config] of agentConfigs) { + if (agentToolContexts.has(agentId)) { + continue; + } + agentToolContexts.set(agentId, { + agent: config, + toolRegistry: config.toolRegistry, + userMCPAuthMap: config.userMCPAuthMap, + tool_resources: config.tool_resources, + actionsEnabled: config.actionsEnabled, + }); + } + // Ensure edges is an array when we have multiple agents (multi-agent mode) // MultiAgentGraph.categorizeEdges requires edges to be iterable if (agentConfigs.size > 0 && !edges) { diff --git a/api/server/services/Endpoints/agents/initialize.spec.js b/api/server/services/Endpoints/agents/initialize.spec.js new file mode 100644 index 0000000000..8027744965 --- /dev/null +++ b/api/server/services/Endpoints/agents/initialize.spec.js @@ -0,0 +1,201 @@ +const mongoose = require('mongoose'); +const { + ResourceType, + PermissionBits, + PrincipalType, + PrincipalModel, +} = require('librechat-data-provider'); +const { MongoMemoryServer } = require('mongodb-memory-server'); + +const mockInitializeAgent = jest.fn(); +const mockValidateAgentModel = jest.fn(); + +jest.mock('@librechat/agents', () => ({ + ...jest.requireActual('@librechat/agents'), + createContentAggregator: jest.fn(() => ({ + contentParts: [], + aggregateContent: jest.fn(), + })), +})); + +jest.mock('@librechat/api', () => ({ + ...jest.requireActual('@librechat/api'), + initializeAgent: (...args) => mockInitializeAgent(...args), + validateAgentModel: (...args) => mockValidateAgentModel(...args), + GenerationJobManager: { setCollectedUsage: jest.fn() }, + getCustomEndpointConfig: jest.fn(), + createSequentialChainEdges: jest.fn(), +})); + +jest.mock('~/server/controllers/agents/callbacks', () => ({ + createToolEndCallback: jest.fn(() => jest.fn()), + getDefaultHandlers: jest.fn(() => ({})), +})); + +jest.mock('~/server/services/ToolService', () => ({ + loadAgentTools: jest.fn(), + loadToolsForExecution: jest.fn(), +})); + +jest.mock('~/server/controllers/ModelController', () => ({ + getModelsConfig: jest.fn().mockResolvedValue({}), +})); + +let agentClientArgs; +jest.mock('~/server/controllers/agents/client', () => { + return jest.fn().mockImplementation((args) => { + agentClientArgs = args; + return {}; + }); +}); + +jest.mock('./addedConvo', () => ({ + processAddedConvo: jest.fn().mockResolvedValue({ userMCPAuthMap: undefined }), +})); + +jest.mock('~/cache', () => ({ + logViolation: jest.fn(), +})); + +const { initializeClient } = require('./initialize'); +const { User, AclEntry } = require('~/db/models'); +const { createAgent } = require('~/models'); + +const PRIMARY_ID = 'agent_primary'; +const TARGET_ID = 'agent_target'; +const AUTHORIZED_ID = 'agent_authorized'; + +describe('initializeClient — processAgent ACL gate', () => { + let mongoServer; + let testUser; + + beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + await mongoose.connect(mongoServer.getUri()); + }); + + afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); + }); + + beforeEach(async () => { + await mongoose.connection.dropDatabase(); + jest.clearAllMocks(); + agentClientArgs = undefined; + + testUser = await User.create({ + email: 'test@example.com', + name: 'Test User', + username: 'testuser', + role: 'USER', + }); + + mockValidateAgentModel.mockResolvedValue({ isValid: true }); + }); + + const makeReq = () => ({ + user: { id: testUser._id.toString(), role: 'USER' }, + body: { conversationId: 'conv_1', files: [] }, + config: { endpoints: {} }, + _resumableStreamId: null, + }); + + const makeEndpointOption = () => ({ + agent: Promise.resolve({ + id: PRIMARY_ID, + name: 'Primary', + provider: 'openai', + model: 'gpt-4', + tools: [], + }), + model_parameters: { model: 'gpt-4' }, + endpoint: 'agents', + }); + + const makePrimaryConfig = (edges) => ({ + id: PRIMARY_ID, + endpoint: 'agents', + edges, + toolDefinitions: [], + toolRegistry: new Map(), + userMCPAuthMap: null, + tool_resources: {}, + resendFiles: true, + maxContextTokens: 4096, + }); + + it('should skip handoff agent and filter its edge when user lacks VIEW access', async () => { + await createAgent({ + id: TARGET_ID, + name: 'Target Agent', + provider: 'openai', + model: 'gpt-4', + author: new mongoose.Types.ObjectId(), + tools: [], + }); + + const edges = [{ from: PRIMARY_ID, to: TARGET_ID, edgeType: 'handoff' }]; + mockInitializeAgent.mockResolvedValue(makePrimaryConfig(edges)); + + await initializeClient({ + req: makeReq(), + res: {}, + signal: new AbortController().signal, + endpointOption: makeEndpointOption(), + }); + + expect(mockInitializeAgent).toHaveBeenCalledTimes(1); + expect(agentClientArgs.agent.edges).toEqual([]); + }); + + it('should initialize handoff agent and keep its edge when user has VIEW access', async () => { + const authorizedAgent = await createAgent({ + id: AUTHORIZED_ID, + name: 'Authorized Agent', + provider: 'openai', + model: 'gpt-4', + author: new mongoose.Types.ObjectId(), + tools: [], + }); + + await AclEntry.create({ + principalType: PrincipalType.USER, + principalId: testUser._id, + principalModel: PrincipalModel.USER, + resourceType: ResourceType.AGENT, + resourceId: authorizedAgent._id, + permBits: PermissionBits.VIEW, + grantedBy: testUser._id, + }); + + const edges = [{ from: PRIMARY_ID, to: AUTHORIZED_ID, edgeType: 'handoff' }]; + const handoffConfig = { + id: AUTHORIZED_ID, + edges: [], + toolDefinitions: [], + toolRegistry: new Map(), + userMCPAuthMap: null, + tool_resources: {}, + }; + + let callCount = 0; + mockInitializeAgent.mockImplementation(() => { + callCount++; + return callCount === 1 + ? Promise.resolve(makePrimaryConfig(edges)) + : Promise.resolve(handoffConfig); + }); + + await initializeClient({ + req: makeReq(), + res: {}, + signal: new AbortController().signal, + endpointOption: makeEndpointOption(), + }); + + expect(mockInitializeAgent).toHaveBeenCalledTimes(2); + expect(agentClientArgs.agent.edges).toHaveLength(1); + expect(agentClientArgs.agent.edges[0].to).toBe(AUTHORIZED_ID); + }); +}); diff --git a/api/server/services/Endpoints/agents/title.js b/api/server/services/Endpoints/agents/title.js index 1d6d359bd6..b7e1a54e06 100644 --- a/api/server/services/Endpoints/agents/title.js +++ b/api/server/services/Endpoints/agents/title.js @@ -66,12 +66,16 @@ const addTitle = async (req, { text, response, client }) => { await titleCache.set(key, title, 120000); await saveConvo( - req, + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { conversationId: response.conversationId, title, }, - { context: 'api/server/services/Endpoints/agents/title.js' }, + { context: 'api/server/services/Endpoints/agents/title.js', noUpsert: true }, ); } catch (error) { logger.error('Error generating title:', error); diff --git a/api/server/services/Endpoints/assistants/build.js b/api/server/services/Endpoints/assistants/build.js index 00a2abf606..85f7090211 100644 --- a/api/server/services/Endpoints/assistants/build.js +++ b/api/server/services/Endpoints/assistants/build.js @@ -1,6 +1,6 @@ const { removeNullishValues } = require('librechat-data-provider'); const generateArtifactsPrompt = require('~/app/clients/prompts/artifacts'); -const { getAssistant } = require('~/models/Assistant'); +const { getAssistant } = require('~/models'); const buildOptions = async (endpoint, parsedBody) => { const { promptPrefix, assistant_id, iconURL, greeting, spec, artifacts, ...modelOptions } = diff --git a/api/server/services/Endpoints/assistants/title.js b/api/server/services/Endpoints/assistants/title.js index a34de4d1af..b31289eb60 100644 --- a/api/server/services/Endpoints/assistants/title.js +++ b/api/server/services/Endpoints/assistants/title.js @@ -1,9 +1,9 @@ const { isEnabled, sanitizeTitle } = require('@librechat/api'); const { logger } = require('@librechat/data-schemas'); const { CacheKeys } = require('librechat-data-provider'); -const { saveConvo } = require('~/models/Conversation'); const getLogStores = require('~/cache/getLogStores'); const initializeClient = require('./initalize'); +const { saveConvo } = require('~/models'); /** * Generates a conversation title using OpenAI SDK @@ -63,25 +63,34 @@ const addTitle = async (req, { text, responseText, conversationId }) => { const title = await generateTitle({ openai, text, responseText }); await titleCache.set(key, title, 120000); + const reqCtx = { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }; await saveConvo( - req, + reqCtx, { conversationId, title, }, - { context: 'api/server/services/Endpoints/assistants/addTitle.js' }, + { context: 'api/server/services/Endpoints/assistants/addTitle.js', noUpsert: true }, ); } catch (error) { logger.error('[addTitle] Error generating title:', error); const fallbackTitle = text.length > 40 ? text.substring(0, 37) + '...' : text; await titleCache.set(key, fallbackTitle, 120000); await saveConvo( - req, + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { conversationId, title: fallbackTitle, }, - { context: 'api/server/services/Endpoints/assistants/addTitle.js' }, + { context: 'api/server/services/Endpoints/assistants/addTitle.js', noUpsert: true }, ); } }; diff --git a/api/server/services/Endpoints/azureAssistants/build.js b/api/server/services/Endpoints/azureAssistants/build.js index 53b1dbeb68..315447ed7f 100644 --- a/api/server/services/Endpoints/azureAssistants/build.js +++ b/api/server/services/Endpoints/azureAssistants/build.js @@ -1,6 +1,6 @@ const { removeNullishValues } = require('librechat-data-provider'); const generateArtifactsPrompt = require('~/app/clients/prompts/artifacts'); -const { getAssistant } = require('~/models/Assistant'); +const { getAssistant } = require('~/models'); const buildOptions = async (endpoint, parsedBody) => { const { promptPrefix, assistant_id, iconURL, greeting, spec, artifacts, ...modelOptions } = diff --git a/api/server/services/Files/Audio/streamAudio.js b/api/server/services/Files/Audio/streamAudio.js index a1d7c7a649..c28a96edff 100644 --- a/api/server/services/Files/Audio/streamAudio.js +++ b/api/server/services/Files/Audio/streamAudio.js @@ -5,8 +5,8 @@ const { parseTextParts, findLastSeparatorIndex, } = require('librechat-data-provider'); -const { getMessage } = require('~/models/Message'); const { getLogStores } = require('~/cache'); +const { getMessage } = require('~/models'); /** * @param {string[]} voiceIds - Array of voice IDs diff --git a/api/server/services/Files/Audio/streamAudio.spec.js b/api/server/services/Files/Audio/streamAudio.spec.js index e76c0849c7..977d8730aa 100644 --- a/api/server/services/Files/Audio/streamAudio.spec.js +++ b/api/server/services/Files/Audio/streamAudio.spec.js @@ -3,7 +3,7 @@ const { createChunkProcessor, splitTextIntoChunks } = require('./streamAudio'); jest.mock('keyv'); const globalCache = {}; -jest.mock('~/models/Message', () => { +jest.mock('~/models', () => { return { getMessage: jest.fn().mockImplementation((messageId) => { return globalCache[messageId] || null; diff --git a/api/server/services/Files/Azure/crud.js b/api/server/services/Files/Azure/crud.js index 25bd749276..8f681bd06c 100644 --- a/api/server/services/Files/Azure/crud.js +++ b/api/server/services/Files/Azure/crud.js @@ -4,7 +4,7 @@ const mime = require('mime'); const axios = require('axios'); const fetch = require('node-fetch'); const { logger } = require('@librechat/data-schemas'); -const { getAzureContainerClient } = require('@librechat/api'); +const { getAzureContainerClient, deleteRagFile } = require('@librechat/api'); const defaultBasePath = 'images'; const { AZURE_STORAGE_PUBLIC_ACCESS = 'true', AZURE_CONTAINER_NAME = 'files' } = process.env; @@ -102,6 +102,8 @@ async function getAzureURL({ fileName, basePath = defaultBasePath, userId, conta * @param {MongoFile} params.file - The file object. */ async function deleteFileFromAzure(req, file) { + await deleteRagFile({ userId: req.user.id, file }); + try { const containerClient = await getAzureContainerClient(AZURE_CONTAINER_NAME); const blobPath = file.filepath.split(`${AZURE_CONTAINER_NAME}/`)[1]; diff --git a/api/server/services/Files/Citations/index.js b/api/server/services/Files/Citations/index.js index 7cb2ee6de0..008e21d7c4 100644 --- a/api/server/services/Files/Citations/index.js +++ b/api/server/services/Files/Citations/index.js @@ -8,8 +8,7 @@ const { EModelEndpoint, PermissionTypes, } = require('librechat-data-provider'); -const { getRoleByName } = require('~/models/Role'); -const { Files } = require('~/models'); +const { getRoleByName, getFiles } = require('~/models'); /** * Process file search results from tool calls @@ -127,7 +126,7 @@ async function enhanceSourcesWithMetadata(sources, appConfig) { let fileMetadataMap = {}; try { - const files = await Files.find({ file_id: { $in: fileIds } }); + const files = await getFiles({ file_id: { $in: fileIds } }); fileMetadataMap = files.reduce((map, file) => { map[file.file_id] = file; return map; diff --git a/api/server/services/Files/Code/__tests__/process-traversal.spec.js b/api/server/services/Files/Code/__tests__/process-traversal.spec.js new file mode 100644 index 0000000000..2db366d06b --- /dev/null +++ b/api/server/services/Files/Code/__tests__/process-traversal.spec.js @@ -0,0 +1,124 @@ +jest.mock('uuid', () => ({ v4: jest.fn(() => 'mock-uuid') })); + +jest.mock('@librechat/data-schemas', () => ({ + logger: { warn: jest.fn(), debug: jest.fn(), error: jest.fn() }, +})); + +jest.mock('@librechat/agents', () => ({ + getCodeBaseURL: jest.fn(() => 'http://localhost:8000'), +})); + +const mockSanitizeFilename = jest.fn(); + +jest.mock('@librechat/api', () => ({ + logAxiosError: jest.fn(), + getBasePath: jest.fn(() => ''), + sanitizeFilename: mockSanitizeFilename, +})); + +jest.mock('librechat-data-provider', () => ({ + ...jest.requireActual('librechat-data-provider'), + mergeFileConfig: jest.fn(() => ({ serverFileSizeLimit: 100 * 1024 * 1024 })), + getEndpointFileConfig: jest.fn(() => ({ + fileSizeLimit: 100 * 1024 * 1024, + supportedMimeTypes: ['*/*'], + })), + fileConfig: { checkType: jest.fn(() => true) }, +})); + +jest.mock('~/models', () => ({ + createFile: jest.fn().mockResolvedValue({}), + getFiles: jest.fn().mockResolvedValue([]), + updateFile: jest.fn(), + claimCodeFile: jest.fn().mockResolvedValue({ file_id: 'mock-uuid', usage: 0 }), +})); + +const mockSaveBuffer = jest.fn().mockResolvedValue('/uploads/user123/mock-uuid__output.csv'); + +jest.mock('~/server/services/Files/strategies', () => ({ + getStrategyFunctions: jest.fn(() => ({ + saveBuffer: mockSaveBuffer, + })), +})); + +jest.mock('~/server/services/Files/permissions', () => ({ + filterFilesByAgentAccess: jest.fn().mockResolvedValue([]), +})); + +jest.mock('~/server/services/Files/images/convert', () => ({ + convertImage: jest.fn(), +})); + +jest.mock('~/server/utils', () => ({ + determineFileType: jest.fn().mockResolvedValue({ mime: 'text/csv' }), +})); + +jest.mock('axios', () => + jest.fn().mockResolvedValue({ + data: Buffer.from('file-content'), + }), +); + +const { createFile } = require('~/models'); +const { processCodeOutput } = require('../process'); + +const baseParams = { + req: { + user: { id: 'user123' }, + config: { + fileStrategy: 'local', + imageOutputType: 'webp', + fileConfig: {}, + }, + }, + id: 'code-file-id', + apiKey: 'test-key', + toolCallId: 'tool-1', + conversationId: 'conv-1', + messageId: 'msg-1', + session_id: 'session-1', +}; + +describe('processCodeOutput path traversal protection', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + test('sanitizeFilename is called with the raw artifact name', async () => { + mockSanitizeFilename.mockReturnValueOnce('output.csv'); + await processCodeOutput({ ...baseParams, name: 'output.csv' }); + expect(mockSanitizeFilename).toHaveBeenCalledWith('output.csv'); + }); + + test('sanitized name is used in saveBuffer fileName', async () => { + mockSanitizeFilename.mockReturnValueOnce('sanitized-name.txt'); + await processCodeOutput({ ...baseParams, name: '../../../tmp/poc.txt' }); + + expect(mockSanitizeFilename).toHaveBeenCalledWith('../../../tmp/poc.txt'); + const call = mockSaveBuffer.mock.calls[0][0]; + expect(call.fileName).toBe('mock-uuid__sanitized-name.txt'); + }); + + test('sanitized name is stored as filename in the file record', async () => { + mockSanitizeFilename.mockReturnValueOnce('safe-output.csv'); + await processCodeOutput({ ...baseParams, name: 'unsafe/../../output.csv' }); + + const fileArg = createFile.mock.calls[0][0]; + expect(fileArg.filename).toBe('safe-output.csv'); + }); + + test('sanitized name is used for image file records', async () => { + const { convertImage } = require('~/server/services/Files/images/convert'); + convertImage.mockResolvedValueOnce({ + filepath: '/images/user123/mock-uuid.webp', + bytes: 100, + }); + + mockSanitizeFilename.mockReturnValueOnce('safe-chart.png'); + await processCodeOutput({ ...baseParams, name: '../../../chart.png' }); + + expect(mockSanitizeFilename).toHaveBeenCalledWith('../../../chart.png'); + const fileArg = createFile.mock.calls[0][0]; + expect(fileArg.filename).toBe('safe-chart.png'); + }); +}); diff --git a/api/server/services/Files/Code/process.js b/api/server/services/Files/Code/process.js index 3f0bfcfc87..e878b00255 100644 --- a/api/server/services/Files/Code/process.js +++ b/api/server/services/Files/Code/process.js @@ -3,7 +3,7 @@ const { v4 } = require('uuid'); const axios = require('axios'); const { logger } = require('@librechat/data-schemas'); const { getCodeBaseURL } = require('@librechat/agents'); -const { logAxiosError, getBasePath } = require('@librechat/api'); +const { logAxiosError, getBasePath, sanitizeFilename } = require('@librechat/api'); const { Tools, megabyte, @@ -146,6 +146,13 @@ const processCodeOutput = async ({ ); } + const safeName = sanitizeFilename(name); + if (safeName !== name) { + logger.warn( + `[processCodeOutput] Filename sanitized: "${name}" -> "${safeName}" | conv=${conversationId}`, + ); + } + if (isImage) { const usage = isUpdate ? (claimed.usage ?? 0) + 1 : 1; const _file = await convertImage(req, buffer, 'high', `${file_id}${fileExt}`); @@ -156,7 +163,7 @@ const processCodeOutput = async ({ file_id, messageId, usage, - filename: name, + filename: safeName, conversationId, user: req.user.id, type: `image/${appConfig.imageOutputType}`, @@ -200,7 +207,7 @@ const processCodeOutput = async ({ ); } - const fileName = `${file_id}__${name}`; + const fileName = `${file_id}__${safeName}`; const filepath = await saveBuffer({ userId: req.user.id, buffer, @@ -213,7 +220,7 @@ const processCodeOutput = async ({ filepath, messageId, object: 'file', - filename: name, + filename: safeName, type: mimeType, conversationId, user: req.user.id, @@ -229,6 +236,11 @@ const processCodeOutput = async ({ await createFile(file, true); return Object.assign(file, { messageId, toolCallId }); } catch (error) { + if (error?.message === 'Path traversal detected in filename') { + logger.warn( + `[processCodeOutput] Path traversal blocked for file "${name}" | conv=${conversationId}`, + ); + } logAxiosError({ message: 'Error downloading/processing code environment file', error, diff --git a/api/server/services/Files/Code/process.spec.js b/api/server/services/Files/Code/process.spec.js index f01a623f90..b89a6c6307 100644 --- a/api/server/services/Files/Code/process.spec.js +++ b/api/server/services/Files/Code/process.spec.js @@ -58,6 +58,7 @@ jest.mock('@librechat/agents', () => ({ jest.mock('@librechat/api', () => ({ logAxiosError: jest.fn(), getBasePath: jest.fn(() => ''), + sanitizeFilename: jest.fn((name) => name), })); // Mock models diff --git a/api/server/services/Files/Firebase/crud.js b/api/server/services/Files/Firebase/crud.js index 170df45677..d5e5a409bf 100644 --- a/api/server/services/Files/Firebase/crud.js +++ b/api/server/services/Files/Firebase/crud.js @@ -3,7 +3,7 @@ const path = require('path'); const axios = require('axios'); const fetch = require('node-fetch'); const { logger } = require('@librechat/data-schemas'); -const { getFirebaseStorage } = require('@librechat/api'); +const { getFirebaseStorage, deleteRagFile } = require('@librechat/api'); const { ref, uploadBytes, getDownloadURL, deleteObject } = require('firebase/storage'); const { getBufferMetadata } = require('~/server/utils'); @@ -167,27 +167,7 @@ function extractFirebaseFilePath(urlString) { * Throws an error if there is an issue with deletion. */ const deleteFirebaseFile = async (req, file) => { - if (file.embedded && process.env.RAG_API_URL) { - const jwtToken = req.headers.authorization.split(' ')[1]; - try { - await axios.delete(`${process.env.RAG_API_URL}/documents`, { - headers: { - Authorization: `Bearer ${jwtToken}`, - 'Content-Type': 'application/json', - accept: 'application/json', - }, - data: [file.file_id], - }); - } catch (error) { - if (error.response?.status === 404) { - logger.warn( - `[deleteFirebaseFile] Document ${file.file_id} not found in RAG API, may have been deleted already`, - ); - } else { - logger.error('[deleteFirebaseFile] Error deleting document from RAG API:', error); - } - } - } + await deleteRagFile({ userId: req.user.id, file }); const fileName = extractFirebaseFilePath(file.filepath); if (!fileName.includes(req.user.id)) { diff --git a/api/server/services/Files/Local/__tests__/crud-traversal.spec.js b/api/server/services/Files/Local/__tests__/crud-traversal.spec.js new file mode 100644 index 0000000000..57ba221d68 --- /dev/null +++ b/api/server/services/Files/Local/__tests__/crud-traversal.spec.js @@ -0,0 +1,69 @@ +jest.mock('@librechat/api', () => ({ deleteRagFile: jest.fn() })); +jest.mock('@librechat/data-schemas', () => ({ + logger: { warn: jest.fn(), error: jest.fn() }, +})); + +const mockTmpBase = require('fs').mkdtempSync( + require('path').join(require('os').tmpdir(), 'crud-traversal-'), +); + +jest.mock('~/config/paths', () => { + const path = require('path'); + return { + publicPath: path.join(mockTmpBase, 'public'), + uploads: path.join(mockTmpBase, 'uploads'), + }; +}); + +const fs = require('fs'); +const path = require('path'); +const { saveLocalBuffer } = require('../crud'); + +describe('saveLocalBuffer path containment', () => { + beforeAll(() => { + fs.mkdirSync(path.join(mockTmpBase, 'public', 'images'), { recursive: true }); + fs.mkdirSync(path.join(mockTmpBase, 'uploads'), { recursive: true }); + }); + + afterAll(() => { + fs.rmSync(mockTmpBase, { recursive: true, force: true }); + }); + + test('rejects filenames with path traversal sequences', async () => { + await expect( + saveLocalBuffer({ + userId: 'user1', + buffer: Buffer.from('malicious'), + fileName: '../../../etc/passwd', + basePath: 'uploads', + }), + ).rejects.toThrow('Path traversal detected in filename'); + }); + + test('rejects prefix-collision traversal (startsWith bypass)', async () => { + fs.mkdirSync(path.join(mockTmpBase, 'uploads', 'user10'), { recursive: true }); + await expect( + saveLocalBuffer({ + userId: 'user1', + buffer: Buffer.from('malicious'), + fileName: '../user10/evil', + basePath: 'uploads', + }), + ).rejects.toThrow('Path traversal detected in filename'); + }); + + test('allows normal filenames', async () => { + const result = await saveLocalBuffer({ + userId: 'user1', + buffer: Buffer.from('safe content'), + fileName: 'file-id__output.csv', + basePath: 'uploads', + }); + + expect(result).toBe('/uploads/user1/file-id__output.csv'); + + const filePath = path.join(mockTmpBase, 'uploads', 'user1', 'file-id__output.csv'); + expect(fs.existsSync(filePath)).toBe(true); + fs.unlinkSync(filePath); + }); +}); diff --git a/api/server/services/Files/Local/crud.js b/api/server/services/Files/Local/crud.js index b43ab75326..c86774d472 100644 --- a/api/server/services/Files/Local/crud.js +++ b/api/server/services/Files/Local/crud.js @@ -1,9 +1,9 @@ const fs = require('fs'); const path = require('path'); const axios = require('axios'); +const { deleteRagFile } = require('@librechat/api'); const { logger } = require('@librechat/data-schemas'); const { EModelEndpoint } = require('librechat-data-provider'); -const { generateShortLivedToken } = require('@librechat/api'); const { resizeImageBuffer } = require('~/server/services/Files/images/resize'); const { getBufferMetadata } = require('~/server/utils'); const paths = require('~/config/paths'); @@ -78,7 +78,13 @@ async function saveLocalBuffer({ userId, buffer, fileName, basePath = 'images' } fs.mkdirSync(directoryPath, { recursive: true }); } - fs.writeFileSync(path.join(directoryPath, fileName), buffer); + const resolvedDir = path.resolve(directoryPath); + const resolvedPath = path.resolve(resolvedDir, fileName); + const rel = path.relative(resolvedDir, resolvedPath); + if (rel.startsWith('..') || path.isAbsolute(rel) || rel.includes(`..${path.sep}`)) { + throw new Error('Path traversal detected in filename'); + } + fs.writeFileSync(resolvedPath, buffer); const filePath = path.posix.join('/', basePath, userId, fileName); @@ -165,9 +171,8 @@ async function getLocalFileURL({ fileName, basePath = 'images' }) { } /** - * Validates if a given filepath is within a specified subdirectory under a base path. This function constructs - * the expected base path using the base, subfolder, and user id from the request, and then checks if the - * provided filepath starts with this constructed base path. + * Validates that a filepath is strictly contained within a subdirectory under a base path, + * using path.relative to prevent prefix-collision bypasses. * * @param {ServerRequest} req - The request object from Express. It should contain a `user` property with an `id`. * @param {string} base - The base directory path. @@ -180,7 +185,8 @@ async function getLocalFileURL({ fileName, basePath = 'images' }) { const isValidPath = (req, base, subfolder, filepath) => { const normalizedBase = path.resolve(base, subfolder, req.user.id); const normalizedFilepath = path.resolve(filepath); - return normalizedFilepath.startsWith(normalizedBase); + const rel = path.relative(normalizedBase, normalizedFilepath); + return !rel.startsWith('..') && !path.isAbsolute(rel) && !rel.includes(`..${path.sep}`); }; /** @@ -213,27 +219,7 @@ const deleteLocalFile = async (req, file) => { /** Filepath stripped of query parameters (e.g., ?manual=true) */ const cleanFilepath = file.filepath.split('?')[0]; - if (file.embedded && process.env.RAG_API_URL) { - const jwtToken = generateShortLivedToken(req.user.id); - try { - await axios.delete(`${process.env.RAG_API_URL}/documents`, { - headers: { - Authorization: `Bearer ${jwtToken}`, - 'Content-Type': 'application/json', - accept: 'application/json', - }, - data: [file.file_id], - }); - } catch (error) { - if (error.response?.status === 404) { - logger.warn( - `[deleteLocalFile] Document ${file.file_id} not found in RAG API, may have been deleted already`, - ); - } else { - logger.error('[deleteLocalFile] Error deleting document from RAG API:', error); - } - } - } + await deleteRagFile({ userId: req.user.id, file }); if (cleanFilepath.startsWith(`/uploads/${req.user.id}`)) { const userUploadDir = path.join(uploads, req.user.id); diff --git a/api/server/services/Files/S3/crud.js b/api/server/services/Files/S3/crud.js deleted file mode 100644 index 8dac767aa2..0000000000 --- a/api/server/services/Files/S3/crud.js +++ /dev/null @@ -1,483 +0,0 @@ -const fs = require('fs'); -const fetch = require('node-fetch'); -const { initializeS3 } = require('@librechat/api'); -const { logger } = require('@librechat/data-schemas'); -const { FileSources } = require('librechat-data-provider'); -const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); -const { - PutObjectCommand, - GetObjectCommand, - HeadObjectCommand, - DeleteObjectCommand, -} = require('@aws-sdk/client-s3'); - -const bucketName = process.env.AWS_BUCKET_NAME; -const defaultBasePath = 'images'; - -let s3UrlExpirySeconds = 2 * 60; // 2 minutes -let s3RefreshExpiryMs = null; - -if (process.env.S3_URL_EXPIRY_SECONDS !== undefined) { - const parsed = parseInt(process.env.S3_URL_EXPIRY_SECONDS, 10); - - if (!isNaN(parsed) && parsed > 0) { - s3UrlExpirySeconds = Math.min(parsed, 7 * 24 * 60 * 60); - } else { - logger.warn( - `[S3] Invalid S3_URL_EXPIRY_SECONDS value: "${process.env.S3_URL_EXPIRY_SECONDS}". Using 2-minute expiry.`, - ); - } -} - -if (process.env.S3_REFRESH_EXPIRY_MS !== null && process.env.S3_REFRESH_EXPIRY_MS) { - const parsed = parseInt(process.env.S3_REFRESH_EXPIRY_MS, 10); - - if (!isNaN(parsed) && parsed > 0) { - s3RefreshExpiryMs = parsed; - logger.info(`[S3] Using custom refresh expiry time: ${s3RefreshExpiryMs}ms`); - } else { - logger.warn( - `[S3] Invalid S3_REFRESH_EXPIRY_MS value: "${process.env.S3_REFRESH_EXPIRY_MS}". Using default refresh logic.`, - ); - } -} - -/** - * Constructs the S3 key based on the base path, user ID, and file name. - */ -const getS3Key = (basePath, userId, fileName) => `${basePath}/${userId}/${fileName}`; - -/** - * Uploads a buffer to S3 and returns a signed URL. - * - * @param {Object} params - * @param {string} params.userId - The user's unique identifier. - * @param {Buffer} params.buffer - The buffer containing file data. - * @param {string} params.fileName - The file name to use in S3. - * @param {string} [params.basePath='images'] - The base path in the bucket. - * @returns {Promise} Signed URL of the uploaded file. - */ -async function saveBufferToS3({ userId, buffer, fileName, basePath = defaultBasePath }) { - const key = getS3Key(basePath, userId, fileName); - const params = { Bucket: bucketName, Key: key, Body: buffer }; - - try { - const s3 = initializeS3(); - await s3.send(new PutObjectCommand(params)); - return await getS3URL({ userId, fileName, basePath }); - } catch (error) { - logger.error('[saveBufferToS3] Error uploading buffer to S3:', error.message); - throw error; - } -} - -/** - * Retrieves a URL for a file stored in S3. - * Returns a signed URL with expiration time or a proxy URL based on config - * - * @param {Object} params - * @param {string} params.userId - The user's unique identifier. - * @param {string} params.fileName - The file name in S3. - * @param {string} [params.basePath='images'] - The base path in the bucket. - * @param {string} [params.customFilename] - Custom filename for Content-Disposition header (overrides extracted filename). - * @param {string} [params.contentType] - Custom content type for the response. - * @returns {Promise} A URL to access the S3 object - */ -async function getS3URL({ - userId, - fileName, - basePath = defaultBasePath, - customFilename = null, - contentType = null, -}) { - const key = getS3Key(basePath, userId, fileName); - const params = { Bucket: bucketName, Key: key }; - - // Add response headers if specified - if (customFilename) { - params.ResponseContentDisposition = `attachment; filename="${customFilename}"`; - } - - if (contentType) { - params.ResponseContentType = contentType; - } - - try { - const s3 = initializeS3(); - return await getSignedUrl(s3, new GetObjectCommand(params), { expiresIn: s3UrlExpirySeconds }); - } catch (error) { - logger.error('[getS3URL] Error getting signed URL from S3:', error.message); - throw error; - } -} - -/** - * Saves a file from a given URL to S3. - * - * @param {Object} params - * @param {string} params.userId - The user's unique identifier. - * @param {string} params.URL - The source URL of the file. - * @param {string} params.fileName - The file name to use in S3. - * @param {string} [params.basePath='images'] - The base path in the bucket. - * @returns {Promise} Signed URL of the uploaded file. - */ -async function saveURLToS3({ userId, URL, fileName, basePath = defaultBasePath }) { - try { - const response = await fetch(URL); - const buffer = await response.buffer(); - // Optionally you can call getBufferMetadata(buffer) if needed. - return await saveBufferToS3({ userId, buffer, fileName, basePath }); - } catch (error) { - logger.error('[saveURLToS3] Error uploading file from URL to S3:', error.message); - throw error; - } -} - -/** - * Deletes a file from S3. - * - * @param {Object} params - * @param {ServerRequest} params.req - * @param {MongoFile} params.file - The file object to delete. - * @returns {Promise} - */ -async function deleteFileFromS3(req, file) { - const key = extractKeyFromS3Url(file.filepath); - const params = { Bucket: bucketName, Key: key }; - if (!key.includes(req.user.id)) { - const message = `[deleteFileFromS3] User ID mismatch: ${req.user.id} vs ${key}`; - logger.error(message); - throw new Error(message); - } - - try { - const s3 = initializeS3(); - - try { - const headCommand = new HeadObjectCommand(params); - await s3.send(headCommand); - logger.debug('[deleteFileFromS3] File exists, proceeding with deletion'); - } catch (headErr) { - if (headErr.name === 'NotFound') { - logger.warn(`[deleteFileFromS3] File does not exist: ${key}`); - return; - } - } - - const deleteResult = await s3.send(new DeleteObjectCommand(params)); - logger.debug('[deleteFileFromS3] Delete command response:', JSON.stringify(deleteResult)); - try { - await s3.send(new HeadObjectCommand(params)); - logger.error('[deleteFileFromS3] File still exists after deletion!'); - } catch (verifyErr) { - if (verifyErr.name === 'NotFound') { - logger.debug(`[deleteFileFromS3] Verified file is deleted: ${key}`); - } else { - logger.error('[deleteFileFromS3] Error verifying deletion:', verifyErr); - } - } - - logger.debug('[deleteFileFromS3] S3 File deletion completed'); - } catch (error) { - logger.error(`[deleteFileFromS3] Error deleting file from S3: ${error.message}`); - logger.error(error.stack); - - // If the file is not found, we can safely return. - if (error.code === 'NoSuchKey') { - return; - } - throw error; - } -} - -/** - * Uploads a local file to S3 by streaming it directly without loading into memory. - * - * @param {Object} params - * @param {import('express').Request} params.req - The Express request (must include user). - * @param {Express.Multer.File} params.file - The file object from Multer. - * @param {string} params.file_id - Unique file identifier. - * @param {string} [params.basePath='images'] - The base path in the bucket. - * @returns {Promise<{ filepath: string, bytes: number }>} - */ -async function uploadFileToS3({ req, file, file_id, basePath = defaultBasePath }) { - try { - const inputFilePath = file.path; - const userId = req.user.id; - const fileName = `${file_id}__${file.originalname}`; - const key = getS3Key(basePath, userId, fileName); - - const stats = await fs.promises.stat(inputFilePath); - const bytes = stats.size; - const fileStream = fs.createReadStream(inputFilePath); - - const s3 = initializeS3(); - const uploadParams = { - Bucket: bucketName, - Key: key, - Body: fileStream, - }; - - await s3.send(new PutObjectCommand(uploadParams)); - const fileURL = await getS3URL({ userId, fileName, basePath }); - return { filepath: fileURL, bytes }; - } catch (error) { - logger.error('[uploadFileToS3] Error streaming file to S3:', error); - try { - if (file && file.path) { - await fs.promises.unlink(file.path); - } - } catch (unlinkError) { - logger.error( - '[uploadFileToS3] Error deleting temporary file, likely already deleted:', - unlinkError.message, - ); - } - throw error; - } -} - -/** - * Extracts the S3 key from a URL or returns the key if already properly formatted - * - * @param {string} fileUrlOrKey - The file URL or key - * @returns {string} The S3 key - */ -function extractKeyFromS3Url(fileUrlOrKey) { - if (!fileUrlOrKey) { - throw new Error('Invalid input: URL or key is empty'); - } - - try { - const url = new URL(fileUrlOrKey); - return url.pathname.substring(1); - } catch (error) { - const parts = fileUrlOrKey.split('/'); - - if (parts.length >= 3 && !fileUrlOrKey.startsWith('http') && !fileUrlOrKey.startsWith('/')) { - return fileUrlOrKey; - } - - return fileUrlOrKey.startsWith('/') ? fileUrlOrKey.substring(1) : fileUrlOrKey; - } -} - -/** - * Retrieves a readable stream for a file stored in S3. - * - * @param {ServerRequest} req - Server request object. - * @param {string} filePath - The S3 key of the file. - * @returns {Promise} - */ -async function getS3FileStream(_req, filePath) { - try { - const Key = extractKeyFromS3Url(filePath); - const params = { Bucket: bucketName, Key }; - const s3 = initializeS3(); - const data = await s3.send(new GetObjectCommand(params)); - return data.Body; // Returns a Node.js ReadableStream. - } catch (error) { - logger.error('[getS3FileStream] Error retrieving S3 file stream:', error); - throw error; - } -} - -/** - * Determines if a signed S3 URL is close to expiration - * - * @param {string} signedUrl - The signed S3 URL - * @param {number} bufferSeconds - Buffer time in seconds - * @returns {boolean} True if the URL needs refreshing - */ -function needsRefresh(signedUrl, bufferSeconds) { - try { - // Parse the URL - const url = new URL(signedUrl); - - // Check if it has the signature parameters that indicate it's a signed URL - // X-Amz-Signature is the most reliable indicator for AWS signed URLs - if (!url.searchParams.has('X-Amz-Signature')) { - // Not a signed URL, so no expiration to check (or it's already a proxy URL) - return false; - } - - // Extract the expiration time from the URL - const expiresParam = url.searchParams.get('X-Amz-Expires'); - const dateParam = url.searchParams.get('X-Amz-Date'); - - if (!expiresParam || !dateParam) { - // Missing expiration information, assume it needs refresh to be safe - return true; - } - - // Parse the AWS date format (YYYYMMDDTHHMMSSZ) - const year = dateParam.substring(0, 4); - const month = dateParam.substring(4, 6); - const day = dateParam.substring(6, 8); - const hour = dateParam.substring(9, 11); - const minute = dateParam.substring(11, 13); - const second = dateParam.substring(13, 15); - - const dateObj = new Date(`${year}-${month}-${day}T${hour}:${minute}:${second}Z`); - const expiresAtDate = new Date(dateObj.getTime() + parseInt(expiresParam) * 1000); - - // Check if it's close to expiration - const now = new Date(); - - // If S3_REFRESH_EXPIRY_MS is set, use it to determine if URL is expired - if (s3RefreshExpiryMs !== null) { - const urlCreationTime = dateObj.getTime(); - const urlAge = now.getTime() - urlCreationTime; - return urlAge >= s3RefreshExpiryMs; - } - - // Otherwise use the default buffer-based logic - const bufferTime = new Date(now.getTime() + bufferSeconds * 1000); - return expiresAtDate <= bufferTime; - } catch (error) { - logger.error('Error checking URL expiration:', error); - // If we can't determine, assume it needs refresh to be safe - return true; - } -} - -/** - * Generates a new URL for an expired S3 URL - * @param {string} currentURL - The current file URL - * @returns {Promise} - */ -async function getNewS3URL(currentURL) { - try { - const s3Key = extractKeyFromS3Url(currentURL); - if (!s3Key) { - return; - } - const keyParts = s3Key.split('/'); - if (keyParts.length < 3) { - return; - } - - const basePath = keyParts[0]; - const userId = keyParts[1]; - const fileName = keyParts.slice(2).join('/'); - - return await getS3URL({ - userId, - fileName, - basePath, - }); - } catch (error) { - logger.error('Error getting new S3 URL:', error); - } -} - -/** - * Refreshes S3 URLs for an array of files if they're expired or close to expiring - * - * @param {MongoFile[]} files - Array of file documents - * @param {(files: MongoFile[]) => Promise} batchUpdateFiles - Function to update files in the database - * @param {number} [bufferSeconds=3600] - Buffer time in seconds to check for expiration - * @returns {Promise} The files with refreshed URLs if needed - */ -async function refreshS3FileUrls(files, batchUpdateFiles, bufferSeconds = 3600) { - if (!files || !Array.isArray(files) || files.length === 0) { - return files; - } - - const filesToUpdate = []; - - for (let i = 0; i < files.length; i++) { - const file = files[i]; - if (!file?.file_id) { - continue; - } - if (file.source !== FileSources.s3) { - continue; - } - if (!file.filepath) { - continue; - } - if (!needsRefresh(file.filepath, bufferSeconds)) { - continue; - } - try { - const newURL = await getNewS3URL(file.filepath); - if (!newURL) { - continue; - } - filesToUpdate.push({ - file_id: file.file_id, - filepath: newURL, - }); - files[i].filepath = newURL; - } catch (error) { - logger.error(`Error refreshing S3 URL for file ${file.file_id}:`, error); - } - } - - if (filesToUpdate.length > 0) { - await batchUpdateFiles(filesToUpdate); - } - - return files; -} - -/** - * Refreshes a single S3 URL if it's expired or close to expiring - * - * @param {{ filepath: string, source: string }} fileObj - Simple file object containing filepath and source - * @param {number} [bufferSeconds=3600] - Buffer time in seconds to check for expiration - * @returns {Promise} The refreshed URL or the original URL if no refresh needed - */ -async function refreshS3Url(fileObj, bufferSeconds = 3600) { - if (!fileObj || fileObj.source !== FileSources.s3 || !fileObj.filepath) { - return fileObj?.filepath || ''; - } - - if (!needsRefresh(fileObj.filepath, bufferSeconds)) { - return fileObj.filepath; - } - - try { - const s3Key = extractKeyFromS3Url(fileObj.filepath); - if (!s3Key) { - logger.warn(`Unable to extract S3 key from URL: ${fileObj.filepath}`); - return fileObj.filepath; - } - - const keyParts = s3Key.split('/'); - if (keyParts.length < 3) { - logger.warn(`Invalid S3 key format: ${s3Key}`); - return fileObj.filepath; - } - - const basePath = keyParts[0]; - const userId = keyParts[1]; - const fileName = keyParts.slice(2).join('/'); - - const newUrl = await getS3URL({ - userId, - fileName, - basePath, - }); - - logger.debug(`Refreshed S3 URL for key: ${s3Key}`); - return newUrl; - } catch (error) { - logger.error(`Error refreshing S3 URL: ${error.message}`); - return fileObj.filepath; - } -} - -module.exports = { - saveBufferToS3, - saveURLToS3, - getS3URL, - deleteFileFromS3, - uploadFileToS3, - getS3FileStream, - refreshS3FileUrls, - refreshS3Url, - needsRefresh, - getNewS3URL, -}; diff --git a/api/server/services/Files/S3/images.js b/api/server/services/Files/S3/images.js deleted file mode 100644 index 9bdae940c3..0000000000 --- a/api/server/services/Files/S3/images.js +++ /dev/null @@ -1,129 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const sharp = require('sharp'); -const { logger } = require('@librechat/data-schemas'); -const { resizeImageBuffer } = require('../images/resize'); -const { updateUser, updateFile } = require('~/models'); -const { saveBufferToS3 } = require('./crud'); - -const defaultBasePath = 'images'; - -/** - * Resizes, converts, and uploads an image file to S3. - * - * @param {Object} params - * @param {import('express').Request} params.req - Express request (expects `user` and `appConfig.imageOutputType`). - * @param {Express.Multer.File} params.file - File object from Multer. - * @param {string} params.file_id - Unique file identifier. - * @param {any} params.endpoint - Endpoint identifier used in image processing. - * @param {string} [params.resolution='high'] - Desired image resolution. - * @param {string} [params.basePath='images'] - Base path in the bucket. - * @returns {Promise<{ filepath: string, bytes: number, width: number, height: number }>} - */ -async function uploadImageToS3({ - req, - file, - file_id, - endpoint, - resolution = 'high', - basePath = defaultBasePath, -}) { - try { - const appConfig = req.config; - const inputFilePath = file.path; - const inputBuffer = await fs.promises.readFile(inputFilePath); - const { - buffer: resizedBuffer, - width, - height, - } = await resizeImageBuffer(inputBuffer, resolution, endpoint); - const extension = path.extname(inputFilePath); - const userId = req.user.id; - - let processedBuffer; - let fileName = `${file_id}__${path.basename(inputFilePath)}`; - const targetExtension = `.${appConfig.imageOutputType}`; - - if (extension.toLowerCase() === targetExtension) { - processedBuffer = resizedBuffer; - } else { - processedBuffer = await sharp(resizedBuffer).toFormat(appConfig.imageOutputType).toBuffer(); - fileName = fileName.replace(new RegExp(path.extname(fileName) + '$'), targetExtension); - if (!path.extname(fileName)) { - fileName += targetExtension; - } - } - - const downloadURL = await saveBufferToS3({ - userId, - buffer: processedBuffer, - fileName, - basePath, - }); - await fs.promises.unlink(inputFilePath); - const bytes = Buffer.byteLength(processedBuffer); - return { filepath: downloadURL, bytes, width, height }; - } catch (error) { - logger.error('[uploadImageToS3] Error uploading image to S3:', error.message); - throw error; - } -} - -/** - * Updates a file record and returns its signed URL. - * - * @param {import('express').Request} req - Express request. - * @param {Object} file - File metadata. - * @returns {Promise<[Promise, string]>} - */ -async function prepareImageURLS3(req, file) { - try { - const updatePromise = updateFile({ file_id: file.file_id }); - return Promise.all([updatePromise, file.filepath]); - } catch (error) { - logger.error('[prepareImageURLS3] Error preparing image URL:', error.message); - throw error; - } -} - -/** - * Processes a user's avatar image by uploading it to S3 and updating the user's avatar URL if required. - * - * @param {Object} params - * @param {Buffer} params.buffer - Avatar image buffer. - * @param {string} params.userId - User's unique identifier. - * @param {string} params.manual - 'true' or 'false' flag for manual update. - * @param {string} [params.agentId] - Optional agent ID if this is an agent avatar. - * @param {string} [params.basePath='images'] - Base path in the bucket. - * @returns {Promise} Signed URL of the uploaded avatar. - */ -async function processS3Avatar({ buffer, userId, manual, agentId, basePath = defaultBasePath }) { - try { - const metadata = await sharp(buffer).metadata(); - const extension = metadata.format === 'gif' ? 'gif' : 'png'; - const timestamp = new Date().getTime(); - - /** Unique filename with timestamp and optional agent ID */ - const fileName = agentId - ? `agent-${agentId}-avatar-${timestamp}.${extension}` - : `avatar-${timestamp}.${extension}`; - - const downloadURL = await saveBufferToS3({ userId, buffer, fileName, basePath }); - - // Only update user record if this is a user avatar (manual === 'true') - if (manual === 'true' && !agentId) { - await updateUser(userId, { avatar: downloadURL }); - } - - return downloadURL; - } catch (error) { - logger.error('[processS3Avatar] Error processing S3 avatar:', error.message); - throw error; - } -} - -module.exports = { - uploadImageToS3, - prepareImageURLS3, - processS3Avatar, -}; diff --git a/api/server/services/Files/S3/index.js b/api/server/services/Files/S3/index.js deleted file mode 100644 index 21e2f2ba7d..0000000000 --- a/api/server/services/Files/S3/index.js +++ /dev/null @@ -1,7 +0,0 @@ -const crud = require('./crud'); -const images = require('./images'); - -module.exports = { - ...crud, - ...images, -}; diff --git a/api/server/services/Files/permissions.js b/api/server/services/Files/permissions.js index d909afe25a..ffa8e74799 100644 --- a/api/server/services/Files/permissions.js +++ b/api/server/services/Files/permissions.js @@ -1,10 +1,29 @@ const { logger } = require('@librechat/data-schemas'); -const { PermissionBits, ResourceType } = require('librechat-data-provider'); +const { PermissionBits, ResourceType, isEphemeralAgentId } = require('librechat-data-provider'); const { checkPermission } = require('~/server/services/PermissionService'); -const { getAgent } = require('~/models/Agent'); +const { getAgent } = require('~/models'); /** - * Checks if a user has access to multiple files through a shared agent (batch operation) + * @param {Object} agent - The agent document (lean) + * @returns {Set} All file IDs attached across all resource types + */ +function getAttachedFileIds(agent) { + const attachedFileIds = new Set(); + if (agent.tool_resources) { + for (const resource of Object.values(agent.tool_resources)) { + if (resource?.file_ids && Array.isArray(resource.file_ids)) { + for (const fileId of resource.file_ids) { + attachedFileIds.add(fileId); + } + } + } + } + return attachedFileIds; +} + +/** + * Checks if a user has access to multiple files through a shared agent (batch operation). + * Access is always scoped to files actually attached to the agent's tool_resources. * @param {Object} params - Parameters object * @param {string} params.userId - The user ID to check access for * @param {string} [params.role] - Optional user role to avoid DB query @@ -16,7 +35,6 @@ const { getAgent } = require('~/models/Agent'); const hasAccessToFilesViaAgent = async ({ userId, role, fileIds, agentId, isDelete }) => { const accessMap = new Map(); - // Initialize all files as no access fileIds.forEach((fileId) => accessMap.set(fileId, false)); try { @@ -26,13 +44,17 @@ const hasAccessToFilesViaAgent = async ({ userId, role, fileIds, agentId, isDele return accessMap; } - // Check if user is the author - if so, grant access to all files + const attachedFileIds = getAttachedFileIds(agent); + if (agent.author.toString() === userId.toString()) { - fileIds.forEach((fileId) => accessMap.set(fileId, true)); + fileIds.forEach((fileId) => { + if (attachedFileIds.has(fileId)) { + accessMap.set(fileId, true); + } + }); return accessMap; } - // Check if user has at least VIEW permission on the agent const hasViewPermission = await checkPermission({ userId, role, @@ -46,7 +68,6 @@ const hasAccessToFilesViaAgent = async ({ userId, role, fileIds, agentId, isDele } if (isDelete) { - // Check if user has EDIT permission (which would indicate collaborative access) const hasEditPermission = await checkPermission({ userId, role, @@ -55,23 +76,11 @@ const hasAccessToFilesViaAgent = async ({ userId, role, fileIds, agentId, isDele requiredPermission: PermissionBits.EDIT, }); - // If user only has VIEW permission, they can't access files - // Only users with EDIT permission or higher can access agent files if (!hasEditPermission) { return accessMap; } } - const attachedFileIds = new Set(); - if (agent.tool_resources) { - for (const [_resourceType, resource] of Object.entries(agent.tool_resources)) { - if (resource?.file_ids && Array.isArray(resource.file_ids)) { - resource.file_ids.forEach((fileId) => attachedFileIds.add(fileId)); - } - } - } - - // Grant access only to files that are attached to this agent fileIds.forEach((fileId) => { if (attachedFileIds.has(fileId)) { accessMap.set(fileId, true); @@ -95,7 +104,7 @@ const hasAccessToFilesViaAgent = async ({ userId, role, fileIds, agentId, isDele * @returns {Promise>} Filtered array of accessible files */ const filterFilesByAgentAccess = async ({ files, userId, role, agentId }) => { - if (!userId || !agentId || !files || files.length === 0) { + if (!userId || !agentId || !files || files.length === 0 || isEphemeralAgentId(agentId)) { return files; } diff --git a/api/server/services/Files/permissions.spec.js b/api/server/services/Files/permissions.spec.js new file mode 100644 index 0000000000..c926e83464 --- /dev/null +++ b/api/server/services/Files/permissions.spec.js @@ -0,0 +1,409 @@ +jest.mock('@librechat/data-schemas', () => ({ + logger: { error: jest.fn() }, +})); + +jest.mock('~/server/services/PermissionService', () => ({ + checkPermission: jest.fn(), +})); + +jest.mock('~/models', () => ({ + getAgent: jest.fn(), +})); + +const { logger } = require('@librechat/data-schemas'); +const { Constants, PermissionBits, ResourceType } = require('librechat-data-provider'); +const { checkPermission } = require('~/server/services/PermissionService'); +const { getAgent } = require('~/models'); +const { filterFilesByAgentAccess, hasAccessToFilesViaAgent } = require('./permissions'); + +const AUTHOR_ID = 'author-user-id'; +const USER_ID = 'viewer-user-id'; +const AGENT_ID = 'agent_test-abc123'; +const AGENT_MONGO_ID = 'mongo-agent-id'; + +function makeFile(file_id, user) { + return { file_id, user, filename: `${file_id}.txt` }; +} + +function makeAgent(overrides = {}) { + return { + _id: AGENT_MONGO_ID, + id: AGENT_ID, + author: AUTHOR_ID, + tool_resources: { + file_search: { file_ids: ['attached-1', 'attached-2'] }, + execute_code: { file_ids: ['attached-3'] }, + }, + ...overrides, + }; +} + +beforeEach(() => { + jest.clearAllMocks(); +}); + +describe('filterFilesByAgentAccess', () => { + describe('early returns (no DB calls)', () => { + it('should return files unfiltered for ephemeral agentId', async () => { + const files = [makeFile('f1', 'other-user')]; + const result = await filterFilesByAgentAccess({ + files, + userId: USER_ID, + agentId: Constants.EPHEMERAL_AGENT_ID, + }); + + expect(result).toBe(files); + expect(getAgent).not.toHaveBeenCalled(); + }); + + it('should return files unfiltered for non-agent_ prefixed agentId', async () => { + const files = [makeFile('f1', 'other-user')]; + const result = await filterFilesByAgentAccess({ + files, + userId: USER_ID, + agentId: 'custom-memory-id', + }); + + expect(result).toBe(files); + expect(getAgent).not.toHaveBeenCalled(); + }); + + it('should return files when userId is missing', async () => { + const files = [makeFile('f1', 'someone')]; + const result = await filterFilesByAgentAccess({ + files, + userId: undefined, + agentId: AGENT_ID, + }); + + expect(result).toBe(files); + expect(getAgent).not.toHaveBeenCalled(); + }); + + it('should return files when agentId is missing', async () => { + const files = [makeFile('f1', 'someone')]; + const result = await filterFilesByAgentAccess({ + files, + userId: USER_ID, + agentId: undefined, + }); + + expect(result).toBe(files); + expect(getAgent).not.toHaveBeenCalled(); + }); + + it('should return empty array when files is empty', async () => { + const result = await filterFilesByAgentAccess({ + files: [], + userId: USER_ID, + agentId: AGENT_ID, + }); + + expect(result).toEqual([]); + expect(getAgent).not.toHaveBeenCalled(); + }); + + it('should return undefined when files is nullish', async () => { + const result = await filterFilesByAgentAccess({ + files: null, + userId: USER_ID, + agentId: AGENT_ID, + }); + + expect(result).toBeNull(); + expect(getAgent).not.toHaveBeenCalled(); + }); + }); + + describe('all files owned by userId', () => { + it('should return all files without calling getAgent', async () => { + const files = [makeFile('f1', USER_ID), makeFile('f2', USER_ID)]; + const result = await filterFilesByAgentAccess({ + files, + userId: USER_ID, + agentId: AGENT_ID, + }); + + expect(result).toEqual(files); + expect(getAgent).not.toHaveBeenCalled(); + }); + }); + + describe('mixed owned and non-owned files', () => { + const ownedFile = makeFile('owned-1', USER_ID); + const sharedFile = makeFile('attached-1', AUTHOR_ID); + const unattachedFile = makeFile('not-attached', AUTHOR_ID); + + it('should return owned + accessible non-owned files when user has VIEW', async () => { + getAgent.mockResolvedValue(makeAgent()); + checkPermission.mockResolvedValue(true); + + const result = await filterFilesByAgentAccess({ + files: [ownedFile, sharedFile, unattachedFile], + userId: USER_ID, + role: 'USER', + agentId: AGENT_ID, + }); + + expect(result).toHaveLength(2); + expect(result.map((f) => f.file_id)).toContain('owned-1'); + expect(result.map((f) => f.file_id)).toContain('attached-1'); + expect(result.map((f) => f.file_id)).not.toContain('not-attached'); + }); + + it('should return only owned files when user lacks VIEW permission', async () => { + getAgent.mockResolvedValue(makeAgent()); + checkPermission.mockResolvedValue(false); + + const result = await filterFilesByAgentAccess({ + files: [ownedFile, sharedFile], + userId: USER_ID, + role: 'USER', + agentId: AGENT_ID, + }); + + expect(result).toEqual([ownedFile]); + }); + + it('should return only owned files when agent is not found', async () => { + getAgent.mockResolvedValue(null); + + const result = await filterFilesByAgentAccess({ + files: [ownedFile, sharedFile], + userId: USER_ID, + agentId: AGENT_ID, + }); + + expect(result).toEqual([ownedFile]); + }); + + it('should return only owned files on DB error (fail-closed)', async () => { + getAgent.mockRejectedValue(new Error('DB connection lost')); + + const result = await filterFilesByAgentAccess({ + files: [ownedFile, sharedFile], + userId: USER_ID, + agentId: AGENT_ID, + }); + + expect(result).toEqual([ownedFile]); + expect(logger.error).toHaveBeenCalled(); + }); + }); + + describe('file with no user field', () => { + it('should treat file as non-owned and run through access check', async () => { + const noUserFile = makeFile('attached-1', undefined); + getAgent.mockResolvedValue(makeAgent()); + checkPermission.mockResolvedValue(true); + + const result = await filterFilesByAgentAccess({ + files: [noUserFile], + userId: USER_ID, + role: 'USER', + agentId: AGENT_ID, + }); + + expect(getAgent).toHaveBeenCalled(); + expect(result).toEqual([noUserFile]); + }); + + it('should exclude file with no user field when not attached to agent', async () => { + const noUserFile = makeFile('not-attached', null); + getAgent.mockResolvedValue(makeAgent()); + checkPermission.mockResolvedValue(true); + + const result = await filterFilesByAgentAccess({ + files: [noUserFile], + userId: USER_ID, + role: 'USER', + agentId: AGENT_ID, + }); + + expect(result).toEqual([]); + }); + }); + + describe('no owned files (all non-owned)', () => { + const file1 = makeFile('attached-1', AUTHOR_ID); + const file2 = makeFile('not-attached', AUTHOR_ID); + + it('should return only attached files when user has VIEW', async () => { + getAgent.mockResolvedValue(makeAgent()); + checkPermission.mockResolvedValue(true); + + const result = await filterFilesByAgentAccess({ + files: [file1, file2], + userId: USER_ID, + role: 'USER', + agentId: AGENT_ID, + }); + + expect(result).toEqual([file1]); + }); + + it('should return empty array when no VIEW permission', async () => { + getAgent.mockResolvedValue(makeAgent()); + checkPermission.mockResolvedValue(false); + + const result = await filterFilesByAgentAccess({ + files: [file1, file2], + userId: USER_ID, + agentId: AGENT_ID, + }); + + expect(result).toEqual([]); + }); + + it('should return empty array when agent not found', async () => { + getAgent.mockResolvedValue(null); + + const result = await filterFilesByAgentAccess({ + files: [file1], + userId: USER_ID, + agentId: AGENT_ID, + }); + + expect(result).toEqual([]); + }); + }); +}); + +describe('hasAccessToFilesViaAgent', () => { + describe('agent not found', () => { + it('should return all-false map', async () => { + getAgent.mockResolvedValue(null); + + const result = await hasAccessToFilesViaAgent({ + userId: USER_ID, + fileIds: ['f1', 'f2'], + agentId: AGENT_ID, + }); + + expect(result.get('f1')).toBe(false); + expect(result.get('f2')).toBe(false); + }); + }); + + describe('author path', () => { + it('should grant access to attached files for the agent author', async () => { + getAgent.mockResolvedValue(makeAgent()); + + const result = await hasAccessToFilesViaAgent({ + userId: AUTHOR_ID, + fileIds: ['attached-1', 'not-attached'], + agentId: AGENT_ID, + }); + + expect(result.get('attached-1')).toBe(true); + expect(result.get('not-attached')).toBe(false); + expect(checkPermission).not.toHaveBeenCalled(); + }); + }); + + describe('VIEW permission path', () => { + it('should grant access to attached files for viewer with VIEW permission', async () => { + getAgent.mockResolvedValue(makeAgent()); + checkPermission.mockResolvedValue(true); + + const result = await hasAccessToFilesViaAgent({ + userId: USER_ID, + role: 'USER', + fileIds: ['attached-1', 'attached-3', 'not-attached'], + agentId: AGENT_ID, + }); + + expect(result.get('attached-1')).toBe(true); + expect(result.get('attached-3')).toBe(true); + expect(result.get('not-attached')).toBe(false); + + expect(checkPermission).toHaveBeenCalledWith({ + userId: USER_ID, + role: 'USER', + resourceType: ResourceType.AGENT, + resourceId: AGENT_MONGO_ID, + requiredPermission: PermissionBits.VIEW, + }); + }); + + it('should deny all when VIEW permission is missing', async () => { + getAgent.mockResolvedValue(makeAgent()); + checkPermission.mockResolvedValue(false); + + const result = await hasAccessToFilesViaAgent({ + userId: USER_ID, + fileIds: ['attached-1'], + agentId: AGENT_ID, + }); + + expect(result.get('attached-1')).toBe(false); + }); + }); + + describe('delete path (EDIT permission required)', () => { + it('should grant access when both VIEW and EDIT pass', async () => { + getAgent.mockResolvedValue(makeAgent()); + checkPermission.mockResolvedValueOnce(true).mockResolvedValueOnce(true); + + const result = await hasAccessToFilesViaAgent({ + userId: USER_ID, + fileIds: ['attached-1'], + agentId: AGENT_ID, + isDelete: true, + }); + + expect(result.get('attached-1')).toBe(true); + expect(checkPermission).toHaveBeenCalledTimes(2); + expect(checkPermission).toHaveBeenLastCalledWith( + expect.objectContaining({ requiredPermission: PermissionBits.EDIT }), + ); + }); + + it('should deny all when VIEW passes but EDIT fails', async () => { + getAgent.mockResolvedValue(makeAgent()); + checkPermission.mockResolvedValueOnce(true).mockResolvedValueOnce(false); + + const result = await hasAccessToFilesViaAgent({ + userId: USER_ID, + fileIds: ['attached-1'], + agentId: AGENT_ID, + isDelete: true, + }); + + expect(result.get('attached-1')).toBe(false); + }); + }); + + describe('error handling', () => { + it('should return all-false map on DB error (fail-closed)', async () => { + getAgent.mockRejectedValue(new Error('connection refused')); + + const result = await hasAccessToFilesViaAgent({ + userId: USER_ID, + fileIds: ['f1', 'f2'], + agentId: AGENT_ID, + }); + + expect(result.get('f1')).toBe(false); + expect(result.get('f2')).toBe(false); + expect(logger.error).toHaveBeenCalledWith( + '[hasAccessToFilesViaAgent] Error checking file access:', + expect.any(Error), + ); + }); + }); + + describe('agent with no tool_resources', () => { + it('should deny all files even for the author', async () => { + getAgent.mockResolvedValue(makeAgent({ tool_resources: undefined })); + + const result = await hasAccessToFilesViaAgent({ + userId: AUTHOR_ID, + fileIds: ['f1'], + agentId: AGENT_ID, + }); + + expect(result.get('f1')).toBe(false); + }); + }); +}); diff --git a/api/server/services/Files/process.js b/api/server/services/Files/process.js index 30b47f2e52..f7d7731975 100644 --- a/api/server/services/Files/process.js +++ b/api/server/services/Files/process.js @@ -16,6 +16,7 @@ const { removeNullishValues, isAssistantsEndpoint, getEndpointFileConfig, + documentParserMimeTypes, } = require('librechat-data-provider'); const { EnvVar } = require('@librechat/agents'); const { logger } = require('@librechat/data-schemas'); @@ -26,16 +27,15 @@ const { resizeImageBuffer, } = require('~/server/services/Files/images'); const { addResourceFileId, deleteResourceFileId } = require('~/server/controllers/assistants/v2'); -const { addAgentResourceFile, removeAgentResourceFiles } = require('~/models/Agent'); const { getOpenAIClient } = require('~/server/controllers/assistants/helpers'); const { loadAuthValues } = require('~/server/services/Tools/credentials'); -const { createFile, updateFileUsage, deleteFiles } = require('~/models'); const { getFileStrategy } = require('~/server/utils/getFileStrategy'); const { checkCapability } = require('~/server/services/Config'); const { LB_QueueAsyncCall } = require('~/server/utils/queue'); const { getStrategyFunctions } = require('./strategies'); const { determineFileType } = require('~/server/utils'); const { STTService } = require('./Audio/STTService'); +const db = require('~/models'); /** * Creates a modular file upload wrapper that ensures filename sanitization @@ -210,7 +210,7 @@ const processDeleteRequest = async ({ req, files }) => { if (agentFiles.length > 0) { promises.push( - removeAgentResourceFiles({ + db.removeAgentResourceFiles({ agent_id: req.body.agent_id, files: agentFiles, }), @@ -218,7 +218,7 @@ const processDeleteRequest = async ({ req, files }) => { } await Promise.allSettled(promises); - await deleteFiles(resolvedFileIds); + await db.deleteFiles(resolvedFileIds); }; /** @@ -250,7 +250,7 @@ const processFileURL = async ({ fileStrategy, userId, URL, fileName, basePath, c dimensions = {}, } = (await saveURL({ userId, URL, fileName, basePath })) || {}; const filepath = await getFileURL({ fileName: `${userId}/${fileName}`, basePath }); - return await createFile( + return await db.createFile( { user: userId, file_id: v4(), @@ -296,7 +296,7 @@ const processImageFile = async ({ req, res, metadata, returnFile = false }) => { endpoint, }); - const result = await createFile( + const result = await db.createFile( { user: req.user.id, file_id, @@ -348,7 +348,7 @@ const uploadImageBuffer = async ({ req, context, metadata = {}, resize = true }) } const fileName = `${file_id}-${filename}`; const filepath = await saveBuffer({ userId: req.user.id, fileName, buffer }); - return await createFile( + return await db.createFile( { user: req.user.id, file_id, @@ -434,7 +434,7 @@ const processFileUpload = async ({ req, res, metadata }) => { filepath = result.filepath; } - const result = await createFile( + const result = await db.createFile( { user: req.user.id, file_id: id ?? file_id, @@ -523,6 +523,12 @@ const processAgentFileUpload = async ({ req, res, metadata }) => { * @return {Promise} */ const createTextFile = async ({ text, bytes, filepath, type = 'text/plain' }) => { + const textBytes = Buffer.byteLength(text, 'utf8'); + if (textBytes > 15 * megabyte) { + throw new Error( + `Extracted text from "${file.originalname}" exceeds the 15MB storage limit (${Math.round(textBytes / megabyte)}MB). Try a shorter document.`, + ); + } const fileInfo = removeNullishValues({ text, bytes, @@ -538,14 +544,14 @@ const processAgentFileUpload = async ({ req, res, metadata }) => { }); if (!messageAttachment && tool_resource) { - await addAgentResourceFile({ - req, + await db.addAgentResourceFile({ file_id, agent_id, tool_resource, + updatingUserId: req?.user?.id, }); } - const result = await createFile(fileInfo, true); + const result = await db.createFile(fileInfo, true); return res .status(200) .json({ message: 'Agent file uploaded and processed successfully', ...result }); @@ -553,29 +559,52 @@ const processAgentFileUpload = async ({ req, res, metadata }) => { const fileConfig = mergeFileConfig(appConfig.fileConfig); - const shouldUseOCR = + const shouldUseConfiguredOCR = appConfig?.ocr != null && fileConfig.checkType(file.mimetype, fileConfig.ocr?.supportedMimeTypes || []); - if (shouldUseOCR && !(await checkCapability(req, AgentCapabilities.ocr))) { - throw new Error('OCR capability is not enabled for Agents'); - } else if (shouldUseOCR) { + const shouldUseDocumentParser = + !shouldUseConfiguredOCR && documentParserMimeTypes.some((regex) => regex.test(file.mimetype)); + + const shouldUseOCR = shouldUseConfiguredOCR || shouldUseDocumentParser; + + const resolveDocumentText = async () => { + if (shouldUseConfiguredOCR) { + try { + const ocrStrategy = appConfig?.ocr?.strategy ?? FileSources.document_parser; + const { handleFileUpload } = getStrategyFunctions(ocrStrategy); + return await handleFileUpload({ req, file, loadAuthValues }); + } catch (err) { + logger.error( + `[processAgentFileUpload] Configured OCR failed for "${file.originalname}", falling back to document_parser:`, + err, + ); + } + } try { - const { handleFileUpload: uploadOCR } = getStrategyFunctions( - appConfig?.ocr?.strategy ?? FileSources.mistral_ocr, - ); - const { - text, - bytes, - filepath: ocrFileURL, - } = await uploadOCR({ req, file, loadAuthValues }); - return await createTextFile({ text, bytes, filepath: ocrFileURL }); - } catch (ocrError) { + const { handleFileUpload } = getStrategyFunctions(FileSources.document_parser); + return await handleFileUpload({ req, file, loadAuthValues }); + } catch (err) { logger.error( - `[processAgentFileUpload] OCR processing failed for file "${file.originalname}", falling back to text extraction:`, - ocrError, + `[processAgentFileUpload] Document parser failed for "${file.originalname}":`, + err, ); } + }; + + if (shouldUseConfiguredOCR && !(await checkCapability(req, AgentCapabilities.ocr))) { + throw new Error('OCR capability is not enabled for Agents'); + } + + if (shouldUseOCR) { + const ocrResult = await resolveDocumentText(); + if (ocrResult) { + const { text, bytes, filepath: ocrFileURL } = ocrResult; + return await createTextFile({ text, bytes, filepath: ocrFileURL }); + } + throw new Error( + `Unable to extract text from "${file.originalname}". The document may be image-based and requires an OCR service to process.`, + ); } const shouldUseSTT = fileConfig.checkType( @@ -655,11 +684,11 @@ const processAgentFileUpload = async ({ req, res, metadata }) => { let filepath = _filepath; if (!messageAttachment && tool_resource) { - await addAgentResourceFile({ - req, + await db.addAgentResourceFile({ file_id, agent_id, tool_resource, + updatingUserId: req?.user?.id, }); } @@ -690,7 +719,7 @@ const processAgentFileUpload = async ({ req, res, metadata }) => { width, }); - const result = await createFile(fileInfo, true); + const result = await db.createFile(fileInfo, true); res.status(200).json({ message: 'Agent file uploaded and processed successfully', ...result }); }; @@ -736,10 +765,10 @@ const processOpenAIFile = async ({ }; if (saveFile) { - await createFile(file, true); + await db.createFile(file, true); } else if (updateUsage) { try { - await updateFileUsage({ file_id }); + await db.updateFileUsage({ file_id }); } catch (error) { logger.error('Error updating file usage', error); } @@ -777,7 +806,7 @@ const processOpenAIImageOutput = async ({ req, buffer, file_id, filename, fileEx file_id, filename, }; - createFile(file, true); + db.createFile(file, true); return file; }; @@ -921,7 +950,7 @@ async function saveBase64Image( fileName: filename, buffer: image.buffer, }); - return await createFile( + return await db.createFile( { type, source, diff --git a/api/server/services/Files/process.spec.js b/api/server/services/Files/process.spec.js new file mode 100644 index 0000000000..39300161a8 --- /dev/null +++ b/api/server/services/Files/process.spec.js @@ -0,0 +1,344 @@ +jest.mock('uuid', () => ({ v4: jest.fn(() => 'mock-uuid') })); + +jest.mock('@librechat/data-schemas', () => ({ + logger: { warn: jest.fn(), debug: jest.fn(), error: jest.fn() }, +})); + +jest.mock('@librechat/agents', () => ({ + EnvVar: { CODE_API_KEY: 'CODE_API_KEY' }, +})); + +jest.mock('@librechat/api', () => ({ + sanitizeFilename: jest.fn((n) => n), + parseText: jest.fn().mockResolvedValue({ text: '', bytes: 0 }), + processAudioFile: jest.fn(), +})); + +jest.mock('librechat-data-provider', () => ({ + ...jest.requireActual('librechat-data-provider'), + mergeFileConfig: jest.fn(), +})); + +jest.mock('~/server/services/Files/images', () => ({ + convertImage: jest.fn(), + resizeAndConvert: jest.fn(), + resizeImageBuffer: jest.fn(), +})); + +jest.mock('~/server/controllers/assistants/v2', () => ({ + addResourceFileId: jest.fn(), + deleteResourceFileId: jest.fn(), +})); + +jest.mock('~/server/controllers/assistants/helpers', () => ({ + getOpenAIClient: jest.fn(), +})); + +jest.mock('~/server/services/Tools/credentials', () => ({ + loadAuthValues: jest.fn(), +})); + +jest.mock('~/models', () => ({ + createFile: jest.fn().mockResolvedValue({ file_id: 'created-file-id' }), + updateFileUsage: jest.fn(), + deleteFiles: jest.fn(), + addAgentResourceFile: jest.fn().mockResolvedValue({}), + removeAgentResourceFiles: jest.fn(), +})); + +jest.mock('~/server/utils/getFileStrategy', () => ({ + getFileStrategy: jest.fn().mockReturnValue('local'), +})); + +jest.mock('~/server/services/Config', () => ({ + checkCapability: jest.fn().mockResolvedValue(true), +})); + +jest.mock('~/server/utils/queue', () => ({ + LB_QueueAsyncCall: jest.fn(), +})); + +jest.mock('~/server/services/Files/strategies', () => ({ + getStrategyFunctions: jest.fn(), +})); + +jest.mock('~/server/utils', () => ({ + determineFileType: jest.fn(), +})); + +jest.mock('~/server/services/Files/Audio/STTService', () => ({ + STTService: { getInstance: jest.fn() }, +})); + +const { EToolResources, FileSources, AgentCapabilities } = require('librechat-data-provider'); +const { mergeFileConfig } = require('librechat-data-provider'); +const { checkCapability } = require('~/server/services/Config'); +const { getStrategyFunctions } = require('~/server/services/Files/strategies'); +const { processAgentFileUpload } = require('./process'); + +const PDF_MIME = 'application/pdf'; +const DOCX_MIME = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; +const XLSX_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; +const XLS_MIME = 'application/vnd.ms-excel'; +const ODS_MIME = 'application/vnd.oasis.opendocument.spreadsheet'; +const ODT_MIME = 'application/vnd.oasis.opendocument.text'; +const ODP_MIME = 'application/vnd.oasis.opendocument.presentation'; +const ODG_MIME = 'application/vnd.oasis.opendocument.graphics'; + +const makeReq = ({ mimetype = PDF_MIME, ocrConfig = null } = {}) => ({ + user: { id: 'user-123' }, + file: { + path: '/tmp/upload.bin', + originalname: 'upload.bin', + filename: 'upload-uuid.bin', + mimetype, + }, + body: { model: 'gpt-4o' }, + config: { + fileConfig: {}, + fileStrategy: 'local', + ocr: ocrConfig, + }, +}); + +const makeMetadata = () => ({ + agent_id: 'agent-abc', + tool_resource: EToolResources.context, + file_id: 'file-uuid-123', +}); + +const mockRes = { + status: jest.fn().mockReturnThis(), + json: jest.fn().mockReturnValue({}), +}; + +const makeFileConfig = ({ ocrSupportedMimeTypes = [] } = {}) => ({ + checkType: (mime, types) => (types ?? []).includes(mime), + ocr: { supportedMimeTypes: ocrSupportedMimeTypes }, + stt: { supportedMimeTypes: [] }, + text: { supportedMimeTypes: [] }, +}); + +describe('processAgentFileUpload', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockRes.status.mockReturnThis(); + mockRes.json.mockReturnValue({}); + checkCapability.mockResolvedValue(true); + getStrategyFunctions.mockReturnValue({ + handleFileUpload: jest + .fn() + .mockResolvedValue({ text: 'extracted text', bytes: 42, filepath: 'doc://result' }), + }); + mergeFileConfig.mockReturnValue(makeFileConfig()); + }); + + describe('OCR strategy selection', () => { + test.each([ + ['PDF', PDF_MIME], + ['DOCX', DOCX_MIME], + ['XLSX', XLSX_MIME], + ['XLS', XLS_MIME], + ['ODS', ODS_MIME], + ['Excel variant (msexcel)', 'application/msexcel'], + ['Excel variant (x-msexcel)', 'application/x-msexcel'], + ])('uses document_parser automatically for %s when no OCR is configured', async (_, mime) => { + mergeFileConfig.mockReturnValue(makeFileConfig()); + const req = makeReq({ mimetype: mime, ocrConfig: null }); + + await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }); + + expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser); + }); + + test('does not check OCR capability when using automatic document_parser fallback', async () => { + const req = makeReq({ mimetype: PDF_MIME, ocrConfig: null }); + + await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }); + + expect(checkCapability).not.toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr); + expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser); + }); + + test('uses the configured OCR strategy when OCR is set up for the file type', async () => { + mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] })); + const req = makeReq({ + mimetype: PDF_MIME, + ocrConfig: { strategy: FileSources.mistral_ocr }, + }); + + await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }); + + expect(checkCapability).toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr); + expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.mistral_ocr); + }); + + test('uses document_parser as default when OCR is configured but no strategy is specified', async () => { + mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] })); + const req = makeReq({ + mimetype: PDF_MIME, + ocrConfig: { supportedMimeTypes: [PDF_MIME] }, + }); + + await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }); + + expect(checkCapability).toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr); + expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser); + }); + + test('throws when configured OCR capability is not enabled for the agent', async () => { + mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] })); + checkCapability.mockResolvedValue(false); + const req = makeReq({ + mimetype: PDF_MIME, + ocrConfig: { strategy: FileSources.mistral_ocr }, + }); + + await expect( + processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }), + ).rejects.toThrow('OCR capability is not enabled for Agents'); + }); + + test('uses document_parser (no capability check) when OCR capability returns false but no OCR config', async () => { + checkCapability.mockResolvedValue(false); + const req = makeReq({ mimetype: PDF_MIME, ocrConfig: null }); + + await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }); + + expect(checkCapability).not.toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr); + expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser); + }); + + test('uses document_parser when OCR is configured but the file type is not in OCR supported types', async () => { + mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] })); + const req = makeReq({ + mimetype: DOCX_MIME, + ocrConfig: { strategy: FileSources.mistral_ocr }, + }); + + await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }); + + expect(checkCapability).not.toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr); + expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser); + expect(getStrategyFunctions).not.toHaveBeenCalledWith(FileSources.mistral_ocr); + }); + + test('does not invoke any OCR strategy for unsupported MIME types without OCR config', async () => { + const req = makeReq({ mimetype: 'text/plain', ocrConfig: null }); + + await expect( + processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }), + ).rejects.toThrow('File type text/plain is not supported for text parsing.'); + + expect(getStrategyFunctions).not.toHaveBeenCalled(); + }); + + test.each([ + ['ODT', ODT_MIME], + ['ODP', ODP_MIME], + ['ODG', ODG_MIME], + ])('routes %s through configured OCR when OCR supports the type', async (_, mime) => { + mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [mime] })); + const req = makeReq({ + mimetype: mime, + ocrConfig: { strategy: FileSources.mistral_ocr }, + }); + + await processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }); + + expect(checkCapability).toHaveBeenCalledWith(expect.anything(), AgentCapabilities.ocr); + expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.mistral_ocr); + }); + + test('throws instead of falling back to parseText when document_parser fails for a document MIME type', async () => { + getStrategyFunctions.mockReturnValue({ + handleFileUpload: jest.fn().mockRejectedValue(new Error('No text found in document')), + }); + const req = makeReq({ mimetype: PDF_MIME, ocrConfig: null }); + const { parseText } = require('@librechat/api'); + + await expect( + processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }), + ).rejects.toThrow(/image-based and requires an OCR service/); + + expect(parseText).not.toHaveBeenCalled(); + }); + + test('falls back to document_parser when configured OCR fails for a document MIME type', async () => { + mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] })); + const failingUpload = jest.fn().mockRejectedValue(new Error('OCR API returned 500')); + const fallbackUpload = jest + .fn() + .mockResolvedValue({ text: 'parsed text', bytes: 11, filepath: 'doc://result' }); + getStrategyFunctions + .mockReturnValueOnce({ handleFileUpload: failingUpload }) + .mockReturnValueOnce({ handleFileUpload: fallbackUpload }); + const req = makeReq({ + mimetype: PDF_MIME, + ocrConfig: { strategy: FileSources.mistral_ocr }, + }); + + await expect( + processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }), + ).resolves.not.toThrow(); + + expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.mistral_ocr); + expect(getStrategyFunctions).toHaveBeenCalledWith(FileSources.document_parser); + }); + + test('throws when both configured OCR and document_parser fallback fail', async () => { + mergeFileConfig.mockReturnValue(makeFileConfig({ ocrSupportedMimeTypes: [PDF_MIME] })); + getStrategyFunctions.mockReturnValue({ + handleFileUpload: jest.fn().mockRejectedValue(new Error('failure')), + }); + const req = makeReq({ + mimetype: PDF_MIME, + ocrConfig: { strategy: FileSources.mistral_ocr }, + }); + const { parseText } = require('@librechat/api'); + + await expect( + processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }), + ).rejects.toThrow(/image-based and requires an OCR service/); + + expect(parseText).not.toHaveBeenCalled(); + }); + }); + + describe('text size guard', () => { + test('throws before writing to MongoDB when extracted text exceeds 15MB', async () => { + const oversizedText = 'x'.repeat(15 * 1024 * 1024 + 1); + getStrategyFunctions.mockReturnValue({ + handleFileUpload: jest.fn().mockResolvedValue({ + text: oversizedText, + bytes: Buffer.byteLength(oversizedText, 'utf8'), + filepath: 'doc://result', + }), + }); + const req = makeReq({ mimetype: PDF_MIME, ocrConfig: null }); + const { createFile } = require('~/models'); + + await expect( + processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }), + ).rejects.toThrow(/exceeds the 15MB storage limit/); + + expect(createFile).not.toHaveBeenCalled(); + }); + + test('succeeds when extracted text is within the 15MB limit', async () => { + const okText = 'x'.repeat(1024); + getStrategyFunctions.mockReturnValue({ + handleFileUpload: jest.fn().mockResolvedValue({ + text: okText, + bytes: Buffer.byteLength(okText, 'utf8'), + filepath: 'doc://result', + }), + }); + const req = makeReq({ mimetype: PDF_MIME, ocrConfig: null }); + + await expect( + processAgentFileUpload({ req, res: mockRes, metadata: makeMetadata() }), + ).resolves.not.toThrow(); + }); + }); +}); diff --git a/api/server/services/Files/strategies.js b/api/server/services/Files/strategies.js index 2ad526194b..47b39cb87b 100644 --- a/api/server/services/Files/strategies.js +++ b/api/server/services/Files/strategies.js @@ -1,5 +1,13 @@ const { FileSources } = require('librechat-data-provider'); const { + getS3URL, + saveURLToS3, + parseDocument, + uploadFileToS3, + S3ImageService, + saveBufferToS3, + getS3FileStream, + deleteFileFromS3, uploadMistralOCR, uploadAzureMistralOCR, uploadGoogleVertexMistralOCR, @@ -26,17 +34,18 @@ const { processLocalAvatar, getLocalFileStream, } = require('./Local'); -const { - getS3URL, - saveURLToS3, - saveBufferToS3, - getS3FileStream, - uploadImageToS3, - prepareImageURLS3, - deleteFileFromS3, - processS3Avatar, - uploadFileToS3, -} = require('./S3'); +const { resizeImageBuffer } = require('./images/resize'); +const { updateUser, updateFile } = require('~/models'); + +const s3ImageService = new S3ImageService({ + resizeImageBuffer, + updateUser, + updateFile, +}); + +const uploadImageToS3 = (params) => s3ImageService.uploadImageToS3(params); +const prepareImageURLS3 = (_req, file) => s3ImageService.prepareImageURL(file); +const processS3Avatar = (params) => s3ImageService.processAvatar(params); const { saveBufferToAzure, saveURLToAzure, @@ -246,6 +255,26 @@ const vertexMistralOCRStrategy = () => ({ handleFileUpload: uploadGoogleVertexMistralOCR, }); +const documentParserStrategy = () => ({ + /** @type {typeof saveFileFromURL | null} */ + saveURL: null, + /** @type {typeof getLocalFileURL | null} */ + getFileURL: null, + /** @type {typeof saveLocalBuffer | null} */ + saveBuffer: null, + /** @type {typeof processLocalAvatar | null} */ + processAvatar: null, + /** @type {typeof uploadLocalImage | null} */ + handleImageUpload: null, + /** @type {typeof prepareImagesLocal | null} */ + prepareImagePayload: null, + /** @type {typeof deleteLocalFile | null} */ + deleteFile: null, + /** @type {typeof getLocalFileStream | null} */ + getDownloadStream: null, + handleFileUpload: parseDocument, +}); + // Strategy Selector const getStrategyFunctions = (fileSource) => { if (fileSource === FileSources.firebase) { @@ -270,6 +299,8 @@ const getStrategyFunctions = (fileSource) => { return azureMistralOCRStrategy(); } else if (fileSource === FileSources.vertexai_mistral_ocr) { return vertexMistralOCRStrategy(); + } else if (fileSource === FileSources.document_parser) { + return documentParserStrategy(); } else if (fileSource === FileSources.text) { return localStrategy(); // Text files use local strategy } else { diff --git a/api/server/services/GraphTokenService.js b/api/server/services/GraphTokenService.js index d5cd6a94f2..843adbe5a2 100644 --- a/api/server/services/GraphTokenService.js +++ b/api/server/services/GraphTokenService.js @@ -7,7 +7,7 @@ const getLogStores = require('~/cache/getLogStores'); /** * Get Microsoft Graph API token using existing token exchange mechanism * @param {Object} user - User object with OpenID information - * @param {string} accessToken - Current access token from Authorization header + * @param {string} accessToken - Federated access token used as OBO assertion * @param {string} scopes - Graph API scopes for the token * @param {boolean} fromCache - Whether to try getting token from cache first * @returns {Promise} Graph API token response with access_token and expires_in diff --git a/api/server/services/MCP.js b/api/server/services/MCP.js index ad1f9f5cc3..c66eb0b6ef 100644 --- a/api/server/services/MCP.js +++ b/api/server/services/MCP.js @@ -34,6 +34,55 @@ const { reinitMCPServer } = require('./Tools/mcp'); const { getAppConfig } = require('./Config'); const { getLogStores } = require('~/cache'); +const MAX_CACHE_SIZE = 1000; +const lastReconnectAttempts = new Map(); +const RECONNECT_THROTTLE_MS = 10_000; + +const missingToolCache = new Map(); +const MISSING_TOOL_TTL_MS = 10_000; + +function evictStale(map, ttl) { + if (map.size <= MAX_CACHE_SIZE) { + return; + } + const now = Date.now(); + for (const [key, timestamp] of map) { + if (now - timestamp >= ttl) { + map.delete(key); + } + if (map.size <= MAX_CACHE_SIZE) { + return; + } + } +} + +const unavailableMsg = + "This tool's MCP server is temporarily unavailable. Please try again shortly."; + +/** + * @param {string} toolName + * @param {string} serverName + */ +function createUnavailableToolStub(toolName, serverName) { + const normalizedToolKey = `${toolName}${Constants.mcp_delimiter}${normalizeServerName(serverName)}`; + const _call = async () => [unavailableMsg, null]; + const toolInstance = tool(_call, { + schema: { + type: 'object', + properties: { + input: { type: 'string', description: 'Input for the tool' }, + }, + required: [], + }, + name: normalizedToolKey, + description: unavailableMsg, + responseFormat: AgentConstants.CONTENT_AND_ARTIFACT, + }); + toolInstance.mcp = true; + toolInstance.mcpRawServerName = serverName; + return toolInstance; +} + function isEmptyObjectSchema(jsonSchema) { return ( jsonSchema != null && @@ -211,6 +260,17 @@ async function reconnectServer({ logger.debug( `[MCP][reconnectServer] serverName: ${serverName}, user: ${user?.id}, hasUserMCPAuthMap: ${!!userMCPAuthMap}`, ); + + const throttleKey = `${user.id}:${serverName}`; + const now = Date.now(); + const lastAttempt = lastReconnectAttempts.get(throttleKey) ?? 0; + if (now - lastAttempt < RECONNECT_THROTTLE_MS) { + logger.debug(`[MCP][reconnectServer] Throttled reconnect for ${serverName}`); + return null; + } + lastReconnectAttempts.set(throttleKey, now); + evictStale(lastReconnectAttempts, RECONNECT_THROTTLE_MS); + const runId = Constants.USE_PRELIM_RESPONSE_MESSAGE_ID; const flowId = `${user.id}:${serverName}:${Date.now()}`; const flowManager = getFlowStateManager(getLogStores(CacheKeys.FLOWS)); @@ -267,7 +327,7 @@ async function reconnectServer({ userMCPAuthMap, forceNew: true, returnOnOAuth: false, - connectionTimeout: Time.TWO_MINUTES, + connectionTimeout: Time.THIRTY_SECONDS, }); } finally { // Clean up abort handler to prevent memory leaks @@ -330,9 +390,13 @@ async function createMCPTools({ userMCPAuthMap, streamId, }); + if (result === null) { + logger.debug(`[MCP][${serverName}] Reconnect throttled, skipping tool creation.`); + return []; + } if (!result || !result.tools) { logger.warn(`[MCP][${serverName}] Failed to reinitialize MCP server.`); - return; + return []; } const serverTools = []; @@ -402,6 +466,14 @@ async function createMCPTool({ /** @type {LCTool | undefined} */ let toolDefinition = availableTools?.[toolKey]?.function; if (!toolDefinition) { + const cachedAt = missingToolCache.get(toolKey); + if (cachedAt && Date.now() - cachedAt < MISSING_TOOL_TTL_MS) { + logger.debug( + `[MCP][${serverName}][${toolName}] Tool in negative cache, returning unavailable stub.`, + ); + return createUnavailableToolStub(toolName, serverName); + } + logger.warn( `[MCP][${serverName}][${toolName}] Requested tool not found in available tools, re-initializing MCP server.`, ); @@ -415,11 +487,18 @@ async function createMCPTool({ streamId, }); toolDefinition = result?.availableTools?.[toolKey]?.function; + + if (!toolDefinition) { + missingToolCache.set(toolKey, Date.now()); + evictStale(missingToolCache, MISSING_TOOL_TTL_MS); + } } if (!toolDefinition) { - logger.warn(`[MCP][${serverName}][${toolName}] Tool definition not found, cannot create tool.`); - return; + logger.warn( + `[MCP][${serverName}][${toolName}] Tool definition not found, returning unavailable stub.`, + ); + return createUnavailableToolStub(toolName, serverName); } return createToolInstance({ @@ -720,4 +799,5 @@ module.exports = { getMCPSetupData, checkOAuthFlowStatus, getServerConnectionStatus, + createUnavailableToolStub, }; diff --git a/api/server/services/MCP.spec.js b/api/server/services/MCP.spec.js index b2caebc91e..14a9ef90ed 100644 --- a/api/server/services/MCP.spec.js +++ b/api/server/services/MCP.spec.js @@ -45,6 +45,7 @@ const { getMCPSetupData, checkOAuthFlowStatus, getServerConnectionStatus, + createUnavailableToolStub, } = require('./MCP'); jest.mock('./Config', () => ({ @@ -1098,6 +1099,188 @@ describe('User parameter passing tests', () => { }); }); + describe('createUnavailableToolStub', () => { + it('should return a tool whose _call returns a valid CONTENT_AND_ARTIFACT two-tuple', async () => { + const stub = createUnavailableToolStub('myTool', 'myServer'); + // invoke() goes through langchain's base tool, which checks responseFormat. + // CONTENT_AND_ARTIFACT requires [content, artifact] — a bare string would throw: + // "Tool response format is "content_and_artifact" but the output was not a two-tuple" + const result = await stub.invoke({}); + // If we reach here without throwing, the two-tuple format is correct. + // invoke() returns the content portion of [content, artifact] as a string. + expect(result).toContain('temporarily unavailable'); + }); + }); + + describe('negative tool cache and throttle interaction', () => { + it('should cache tool as missing even when throttled (cross-user dedup)', async () => { + const mockUser = { id: 'throttle-test-user' }; + const mockRes = { write: jest.fn(), flush: jest.fn() }; + + // First call: reconnect succeeds but tool not found + mockReinitMCPServer.mockResolvedValueOnce({ + availableTools: {}, + }); + + await createMCPTool({ + res: mockRes, + user: mockUser, + toolKey: `missing-tool${D}cache-dedup-server`, + provider: 'openai', + userMCPAuthMap: {}, + availableTools: undefined, + }); + + // Second call within 10s for DIFFERENT tool on same server: + // reconnect is throttled (returns null), tool is still cached as missing. + // This is intentional: the cache acts as cross-user dedup since the + // throttle is per-user-per-server and can't prevent N different users + // from each triggering their own reconnect. + const result2 = await createMCPTool({ + res: mockRes, + user: mockUser, + toolKey: `other-tool${D}cache-dedup-server`, + provider: 'openai', + userMCPAuthMap: {}, + availableTools: undefined, + }); + + expect(result2).toBeDefined(); + expect(result2.name).toContain('other-tool'); + expect(mockReinitMCPServer).toHaveBeenCalledTimes(1); + }); + + it('should prevent user B from triggering reconnect when user A already cached the tool', async () => { + const userA = { id: 'cache-user-A' }; + const userB = { id: 'cache-user-B' }; + const mockRes = { write: jest.fn(), flush: jest.fn() }; + + // User A: real reconnect, tool not found → cached + mockReinitMCPServer.mockResolvedValueOnce({ + availableTools: {}, + }); + + await createMCPTool({ + res: mockRes, + user: userA, + toolKey: `shared-tool${D}cross-user-server`, + provider: 'openai', + userMCPAuthMap: {}, + availableTools: undefined, + }); + + expect(mockReinitMCPServer).toHaveBeenCalledTimes(1); + + // User B requests the SAME tool within 10s. + // The negative cache is keyed by toolKey (no user prefix), so user B + // gets a cache hit and no reconnect fires. This is the cross-user + // storm protection: without this, user B's unthrottled first request + // would trigger a second reconnect to the same server. + const result = await createMCPTool({ + res: mockRes, + user: userB, + toolKey: `shared-tool${D}cross-user-server`, + provider: 'openai', + userMCPAuthMap: {}, + availableTools: undefined, + }); + + expect(result).toBeDefined(); + expect(result.name).toContain('shared-tool'); + // reinitMCPServer still called only once — user B hit the cache + expect(mockReinitMCPServer).toHaveBeenCalledTimes(1); + }); + + it('should prevent user B from triggering reconnect for throttle-cached tools', async () => { + const userA = { id: 'storm-user-A' }; + const userB = { id: 'storm-user-B' }; + const mockRes = { write: jest.fn(), flush: jest.fn() }; + + // User A: real reconnect for tool-1, tool not found → cached + mockReinitMCPServer.mockResolvedValueOnce({ + availableTools: {}, + }); + + await createMCPTool({ + res: mockRes, + user: userA, + toolKey: `tool-1${D}storm-server`, + provider: 'openai', + userMCPAuthMap: {}, + availableTools: undefined, + }); + + // User A: tool-2 on same server within 10s → throttled → cached from throttle + await createMCPTool({ + res: mockRes, + user: userA, + toolKey: `tool-2${D}storm-server`, + provider: 'openai', + userMCPAuthMap: {}, + availableTools: undefined, + }); + + expect(mockReinitMCPServer).toHaveBeenCalledTimes(1); + + // User B requests tool-2 — gets cache hit from the throttle-cached entry. + // Without this caching, user B would trigger a real reconnect since + // user B has their own throttle key and hasn't reconnected yet. + const result = await createMCPTool({ + res: mockRes, + user: userB, + toolKey: `tool-2${D}storm-server`, + provider: 'openai', + userMCPAuthMap: {}, + availableTools: undefined, + }); + + expect(result).toBeDefined(); + expect(result.name).toContain('tool-2'); + // Still only 1 real reconnect — user B was protected by the cache + expect(mockReinitMCPServer).toHaveBeenCalledTimes(1); + }); + }); + + describe('createMCPTools throttle handling', () => { + it('should return empty array with debug log when reconnect is throttled', async () => { + const mockUser = { id: 'throttle-tools-user' }; + const mockRes = { write: jest.fn(), flush: jest.fn() }; + + // First call: real reconnect + mockReinitMCPServer.mockResolvedValueOnce({ + tools: [{ name: 'tool1' }], + availableTools: { + [`tool1${D}throttle-tools-server`]: { + function: { description: 'Tool 1', parameters: {} }, + }, + }, + }); + + await createMCPTools({ + res: mockRes, + user: mockUser, + serverName: 'throttle-tools-server', + provider: 'openai', + userMCPAuthMap: {}, + }); + + // Second call within 10s — throttled + const result = await createMCPTools({ + res: mockRes, + user: mockUser, + serverName: 'throttle-tools-server', + provider: 'openai', + userMCPAuthMap: {}, + }); + + expect(result).toEqual([]); + // reinitMCPServer called only once — second was throttled + expect(mockReinitMCPServer).toHaveBeenCalledTimes(1); + // Should log at debug level (not warn) for throttled case + expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining('Reconnect throttled')); + }); + }); + describe('User parameter integrity', () => { it('should preserve user object properties through the call chain', async () => { const complexUser = { diff --git a/api/server/services/PermissionService.js b/api/server/services/PermissionService.js index a843f48f6f..2ac00f3afa 100644 --- a/api/server/services/PermissionService.js +++ b/api/server/services/PermissionService.js @@ -15,16 +15,22 @@ const { getEffectivePermissionsForResources: getEffectivePermissionsForResourcesACL, grantPermission: grantPermissionACL, findEntriesByPrincipalsAndResource, + findRolesByResourceType, + findPublicResourceIds, + bulkWriteAclEntries, findGroupByExternalId, findRoleByIdentifier, + deleteAclEntries, getUserPrincipals, + findGroupByQuery, + updateGroupById, + bulkUpdateGroups, hasPermission, createGroup, createUser, updateUser, findUser, } = require('~/models'); -const { AclEntry, AccessRole, Group } = require('~/db/models'); /** @type {boolean|null} */ let transactionSupportCache = null; @@ -275,17 +281,9 @@ const findPubliclyAccessibleResources = async ({ resourceType, requiredPermissio validateResourceType(resourceType); - // Find all public ACL entries where the public principal has at least the required permission bits - const entries = await AclEntry.find({ - principalType: PrincipalType.PUBLIC, - resourceType, - permBits: { $bitsAllSet: requiredPermissions }, - }).distinct('resourceId'); - - return entries; + return await findPublicResourceIds(resourceType, requiredPermissions); } catch (error) { logger.error(`[PermissionService.findPubliclyAccessibleResources] Error: ${error.message}`); - // Re-throw validation errors if (error.message.includes('requiredPermissions must be')) { throw error; } @@ -302,7 +300,7 @@ const findPubliclyAccessibleResources = async ({ resourceType, requiredPermissio const getAvailableRoles = async ({ resourceType }) => { validateResourceType(resourceType); - return await AccessRole.find({ resourceType }).lean(); + return await findRolesByResourceType(resourceType); }; /** @@ -423,7 +421,7 @@ const ensureGroupPrincipalExists = async function (principal, authContext = null let existingGroup = await findGroupByExternalId(principal.idOnTheSource, 'entra'); if (!existingGroup && principal.email) { - existingGroup = await Group.findOne({ email: principal.email.toLowerCase() }).lean(); + existingGroup = await findGroupByQuery({ email: principal.email.toLowerCase() }); } if (existingGroup) { @@ -452,7 +450,7 @@ const ensureGroupPrincipalExists = async function (principal, authContext = null } if (needsUpdate) { - await Group.findByIdAndUpdate(existingGroup._id, { $set: updateData }, { new: true }); + await updateGroupById(existingGroup._id, updateData); } return existingGroup._id.toString(); @@ -520,7 +518,7 @@ const syncUserEntraGroupMemberships = async (user, accessToken, session = null) const sessionOptions = session ? { session } : {}; - await Group.updateMany( + await bulkUpdateGroups( { idOnTheSource: { $in: allGroupIds }, source: 'entra', @@ -530,13 +528,13 @@ const syncUserEntraGroupMemberships = async (user, accessToken, session = null) sessionOptions, ); - await Group.updateMany( + await bulkUpdateGroups( { source: 'entra', memberIds: user.idOnTheSource, idOnTheSource: { $nin: allGroupIds }, }, - { $pull: { memberIds: user.idOnTheSource } }, + { $pullAll: { memberIds: [user.idOnTheSource] } }, sessionOptions, ); } catch (error) { @@ -628,7 +626,7 @@ const bulkUpdateResourcePermissions = async ({ const sessionOptions = localSession ? { session: localSession } : {}; - const roles = await AccessRole.find({ resourceType }).lean(); + const roles = await findRolesByResourceType(resourceType); const rolesMap = new Map(); roles.forEach((role) => { rolesMap.set(role.accessRoleId, role); @@ -732,7 +730,7 @@ const bulkUpdateResourcePermissions = async ({ } if (bulkWrites.length > 0) { - await AclEntry.bulkWrite(bulkWrites, sessionOptions); + await bulkWriteAclEntries(bulkWrites, sessionOptions); } const deleteQueries = []; @@ -773,12 +771,7 @@ const bulkUpdateResourcePermissions = async ({ } if (deleteQueries.length > 0) { - await AclEntry.deleteMany( - { - $or: deleteQueries, - }, - sessionOptions, - ); + await deleteAclEntries({ $or: deleteQueries }, sessionOptions); } if (shouldEndSession && supportsTransactions) { @@ -788,7 +781,15 @@ const bulkUpdateResourcePermissions = async ({ return results; } catch (error) { if (shouldEndSession && supportsTransactions) { - await localSession.abortTransaction(); + try { + await localSession.abortTransaction(); + } catch (transactionError) { + /** best-effort abort; may fail if commit already succeeded */ + logger.error( + `[PermissionService.bulkUpdateResourcePermissions] Error aborting transaction:`, + transactionError, + ); + } } logger.error(`[PermissionService.bulkUpdateResourcePermissions] Error: ${error.message}`); throw error; @@ -814,7 +815,7 @@ const removeAllPermissions = async ({ resourceType, resourceId }) => { throw new Error(`Invalid resource ID: ${resourceId}`); } - const result = await AclEntry.deleteMany({ + const result = await deleteAclEntries({ resourceType, resourceId, }); diff --git a/api/server/services/PermissionService.spec.js b/api/server/services/PermissionService.spec.js index b41780f345..477b0702b9 100644 --- a/api/server/services/PermissionService.spec.js +++ b/api/server/services/PermissionService.spec.js @@ -9,6 +9,7 @@ const { } = require('librechat-data-provider'); const { bulkUpdateResourcePermissions, + syncUserEntraGroupMemberships, getEffectivePermissions, findAccessibleResources, getAvailableRoles, @@ -26,7 +27,11 @@ jest.mock('@librechat/data-schemas', () => ({ // Mock GraphApiService to prevent config loading issues jest.mock('~/server/services/GraphApiService', () => ({ + entraIdPrincipalFeatureEnabled: jest.fn().mockReturnValue(false), + getUserOwnedEntraGroups: jest.fn().mockResolvedValue([]), + getUserEntraGroups: jest.fn().mockResolvedValue([]), getGroupMembers: jest.fn().mockResolvedValue([]), + getGroupOwners: jest.fn().mockResolvedValue([]), })); // Mock the logger @@ -1933,3 +1938,134 @@ describe('PermissionService', () => { }); }); }); + +describe('syncUserEntraGroupMemberships - $pullAll on Group.memberIds', () => { + const { + entraIdPrincipalFeatureEnabled, + getUserEntraGroups, + } = require('~/server/services/GraphApiService'); + const { Group } = require('~/db/models'); + + const userEntraId = 'entra-user-001'; + const user = { + openidId: 'openid-sub-001', + idOnTheSource: userEntraId, + provider: 'openid', + }; + + beforeEach(async () => { + await Group.deleteMany({}); + entraIdPrincipalFeatureEnabled.mockReturnValue(true); + }); + + afterEach(() => { + entraIdPrincipalFeatureEnabled.mockReturnValue(false); + getUserEntraGroups.mockResolvedValue([]); + }); + + it('should add user to matching Entra groups and remove from non-matching ones', async () => { + await Group.create([ + { name: 'Group A', source: 'entra', idOnTheSource: 'entra-group-a', memberIds: [] }, + { + name: 'Group B', + source: 'entra', + idOnTheSource: 'entra-group-b', + memberIds: [userEntraId], + }, + { + name: 'Group C', + source: 'entra', + idOnTheSource: 'entra-group-c', + memberIds: [userEntraId], + }, + ]); + + getUserEntraGroups.mockResolvedValue(['entra-group-a', 'entra-group-c']); + + await syncUserEntraGroupMemberships(user, 'fake-access-token'); + + const groups = await Group.find({ source: 'entra' }).sort({ name: 1 }).lean(); + expect(groups[0].memberIds).toContain(userEntraId); + expect(groups[1].memberIds).not.toContain(userEntraId); + expect(groups[2].memberIds).toContain(userEntraId); + }); + + it('should not modify groups when API returns empty list (early return)', async () => { + await Group.create([ + { + name: 'Group X', + source: 'entra', + idOnTheSource: 'entra-x', + memberIds: [userEntraId, 'other-user'], + }, + { name: 'Group Y', source: 'entra', idOnTheSource: 'entra-y', memberIds: [userEntraId] }, + ]); + + getUserEntraGroups.mockResolvedValue([]); + + await syncUserEntraGroupMemberships(user, 'fake-token'); + + const groups = await Group.find({ source: 'entra' }).sort({ name: 1 }).lean(); + expect(groups[0].memberIds).toContain(userEntraId); + expect(groups[0].memberIds).toContain('other-user'); + expect(groups[1].memberIds).toContain(userEntraId); + }); + + it('should remove user from groups not in the API response via $pullAll', async () => { + await Group.create([ + { name: 'Keep', source: 'entra', idOnTheSource: 'entra-keep', memberIds: [userEntraId] }, + { + name: 'Remove', + source: 'entra', + idOnTheSource: 'entra-remove', + memberIds: [userEntraId, 'other-user'], + }, + ]); + + getUserEntraGroups.mockResolvedValue(['entra-keep']); + + await syncUserEntraGroupMemberships(user, 'fake-token'); + + const keep = await Group.findOne({ idOnTheSource: 'entra-keep' }).lean(); + const remove = await Group.findOne({ idOnTheSource: 'entra-remove' }).lean(); + expect(keep.memberIds).toContain(userEntraId); + expect(remove.memberIds).not.toContain(userEntraId); + expect(remove.memberIds).toContain('other-user'); + }); + + it('should not modify local groups', async () => { + await Group.create([ + { name: 'Local Group', source: 'local', memberIds: [userEntraId] }, + { + name: 'Entra Group', + source: 'entra', + idOnTheSource: 'entra-only', + memberIds: [userEntraId], + }, + ]); + + getUserEntraGroups.mockResolvedValue([]); + + await syncUserEntraGroupMemberships(user, 'fake-token'); + + const localGroup = await Group.findOne({ source: 'local' }).lean(); + expect(localGroup.memberIds).toContain(userEntraId); + }); + + it('should early-return when feature is disabled', async () => { + entraIdPrincipalFeatureEnabled.mockReturnValue(false); + + await Group.create({ + name: 'Should Not Touch', + source: 'entra', + idOnTheSource: 'entra-safe', + memberIds: [userEntraId], + }); + + getUserEntraGroups.mockResolvedValue([]); + await syncUserEntraGroupMemberships(user, 'fake-token'); + + const group = await Group.findOne({ idOnTheSource: 'entra-safe' }).lean(); + expect(group.memberIds).toContain(userEntraId); + }); +}); diff --git a/api/server/services/Threads/manage.js b/api/server/services/Threads/manage.js index 627dba1a35..27520f38a5 100644 --- a/api/server/services/Threads/manage.js +++ b/api/server/services/Threads/manage.js @@ -1,16 +1,15 @@ const path = require('path'); const { v4 } = require('uuid'); -const { countTokens, escapeRegExp } = require('@librechat/api'); +const { countTokens } = require('@librechat/api'); +const { escapeRegExp } = require('@librechat/data-schemas'); const { Constants, ContentTypes, AnnotationTypes, defaultOrderQuery, } = require('librechat-data-provider'); +const { recordMessage, getMessages, spendTokens, saveConvo } = require('~/models'); const { retrieveAndProcessFile } = require('~/server/services/Files/process'); -const { recordMessage, getMessages } = require('~/models/Message'); -const { spendTokens } = require('~/models/spendTokens'); -const { saveConvo } = require('~/models/Conversation'); /** * Initializes a new thread or adds messages to an existing thread. @@ -62,24 +61,6 @@ async function initThread({ openai, body, thread_id: _thread_id }) { async function saveUserMessage(req, params) { const tokenCount = await countTokens(params.text); - // todo: do this on the frontend - // const { file_ids = [] } = params; - // let content; - // if (file_ids.length) { - // content = [ - // { - // value: params.text, - // }, - // ...( - // file_ids - // .filter(f => f) - // .map((file_id) => ({ - // file_id, - // })) - // ), - // ]; - // } - const userMessage = { user: params.user, endpoint: params.endpoint, @@ -110,9 +91,15 @@ async function saveUserMessage(req, params) { } const message = await recordMessage(userMessage); - await saveConvo(req, convo, { - context: 'api/server/services/Threads/manage.js #saveUserMessage', - }); + await saveConvo( + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, + convo, + { context: 'api/server/services/Threads/manage.js #saveUserMessage' }, + ); return message; } @@ -161,7 +148,11 @@ async function saveAssistantMessage(req, params) { }); await saveConvo( - req, + { + userId: req?.user?.id, + isTemporary: req?.body?.isTemporary, + interfaceConfig: req?.config?.interfaceConfig, + }, { endpoint: params.endpoint, conversationId: params.conversationId, @@ -353,7 +344,11 @@ async function syncMessages({ await Promise.all(recordPromises); await saveConvo( - openai.req, + { + userId: openai.req?.user?.id, + isTemporary: openai.req?.body?.isTemporary, + interfaceConfig: openai.req?.config?.interfaceConfig, + }, { conversationId, file_ids: attached_file_ids, diff --git a/api/server/services/ToolService.js b/api/server/services/ToolService.js index eedb95bd4d..ca75e7eb4f 100644 --- a/api/server/services/ToolService.js +++ b/api/server/services/ToolService.js @@ -12,7 +12,6 @@ const { const { sendEvent, getToolkitKey, - hasCustomUserVars, getUserMCPAuthMap, loadToolDefinitions, GenerationJobManager, @@ -43,6 +42,7 @@ const { } = require('librechat-data-provider'); const { createActionTool, + legacyDomainEncode, decryptMetadata, loadActionSets, domainParser, @@ -65,6 +65,28 @@ const { redactMessage } = require('~/config/parsers'); const { findPluginAuthsByKeys } = require('~/models'); const { getFlowStateManager } = require('~/config'); const { getLogStores } = require('~/cache'); + +const domainSeparatorRegex = new RegExp(actionDomainSeparator, 'g'); + +/** + * Resolves the set of enabled agent capabilities from endpoints config, + * falling back to app-level or default capabilities for ephemeral agents. + * @param {ServerRequest} req + * @param {Object} appConfig + * @param {string} agentId + * @returns {Promise>} + */ +async function resolveAgentCapabilities(req, appConfig, agentId) { + const endpointsConfig = await getEndpointsConfig(req); + let capabilities = new Set(endpointsConfig?.[EModelEndpoint.agents]?.capabilities ?? []); + if (capabilities.size === 0 && isEphemeralAgentId(agentId)) { + capabilities = new Set( + appConfig.endpoints?.[EModelEndpoint.agents]?.capabilities ?? defaultAgentCapabilities, + ); + } + return capabilities; +} + /** * Processes the required actions by calling the appropriate tools and returning the outputs. * @param {OpenAIClient} client - OpenAI or StreamRunManager Client. @@ -153,8 +175,7 @@ async function processRequiredActions(client, requiredActions) { const promises = []; - /** @type {Action[]} */ - let actionSets = []; + let actionSetsData = null; let isActionTool = false; const ActionToolMap = {}; const ActionBuildersMap = {}; @@ -240,9 +261,9 @@ async function processRequiredActions(client, requiredActions) { if (!tool) { // throw new Error(`Tool ${currentAction.tool} not found.`); - // Load all action sets once if not already loaded - if (!actionSets.length) { - actionSets = + if (!actionSetsData) { + /** @type {Action[]} */ + const actionSets = (await loadActionSets({ assistant_id: client.req.body.assistant_id, })) ?? []; @@ -250,11 +271,16 @@ async function processRequiredActions(client, requiredActions) { // Process all action sets once // Map domains to their processed action sets const processedDomains = new Map(); - const domainMap = new Map(); + const domainLookupMap = new Map(); for (const action of actionSets) { const domain = await domainParser(action.metadata.domain, true); - domainMap.set(domain, action); + domainLookupMap.set(domain, domain); + + const legacyDomain = legacyDomainEncode(action.metadata.domain); + if (legacyDomain !== domain) { + domainLookupMap.set(legacyDomain, domain); + } const isDomainAllowed = await isActionDomainAllowed( action.metadata.domain, @@ -309,27 +335,26 @@ async function processRequiredActions(client, requiredActions) { ActionBuildersMap[action.metadata.domain] = requestBuilders; } - // Update actionSets reference to use the domain map - actionSets = { domainMap, processedDomains }; + actionSetsData = { domainLookupMap, processedDomains }; } - // Find the matching domain for this tool let currentDomain = ''; - for (const domain of actionSets.domainMap.keys()) { - if (currentAction.tool.includes(domain)) { - currentDomain = domain; + let matchedKey = ''; + for (const [key, canonical] of actionSetsData.domainLookupMap.entries()) { + if (currentAction.tool.includes(key)) { + currentDomain = canonical; + matchedKey = key; break; } } - if (!currentDomain || !actionSets.processedDomains.has(currentDomain)) { - // TODO: try `function` if no action set is found - // throw new Error(`Tool ${currentAction.tool} not found.`); + if (!currentDomain || !actionSetsData.processedDomains.has(currentDomain)) { continue; } - const { action, requestBuilders, encrypted } = actionSets.processedDomains.get(currentDomain); - const functionName = currentAction.tool.replace(`${actionDelimiter}${currentDomain}`, ''); + const { action, requestBuilders, encrypted } = + actionSetsData.processedDomains.get(currentDomain); + const functionName = currentAction.tool.replace(`${actionDelimiter}${matchedKey}`, ''); const requestBuilder = requestBuilders[functionName]; if (!requestBuilder) { @@ -446,17 +471,11 @@ async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null, to } const appConfig = req.config; - const endpointsConfig = await getEndpointsConfig(req); - let enabledCapabilities = new Set(endpointsConfig?.[EModelEndpoint.agents]?.capabilities ?? []); - - if (enabledCapabilities.size === 0 && isEphemeralAgentId(agent.id)) { - enabledCapabilities = new Set( - appConfig.endpoints?.[EModelEndpoint.agents]?.capabilities ?? defaultAgentCapabilities, - ); - } + const enabledCapabilities = await resolveAgentCapabilities(req, appConfig, agent.id); const checkCapability = (capability) => enabledCapabilities.has(capability); const areToolsEnabled = checkCapability(AgentCapabilities.tools); + const actionsEnabled = checkCapability(AgentCapabilities.actions); const deferredToolsEnabled = checkCapability(AgentCapabilities.deferred_tools); const filteredTools = agent.tools?.filter((tool) => { @@ -469,7 +488,10 @@ async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null, to if (tool === Tools.web_search) { return checkCapability(AgentCapabilities.web_search); } - if (!areToolsEnabled && !tool.includes(actionDelimiter)) { + if (tool.includes(actionDelimiter)) { + return actionsEnabled; + } + if (!areToolsEnabled) { return false; } return true; @@ -481,7 +503,7 @@ async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null, to /** @type {Record>} */ let userMCPAuthMap; - if (hasCustomUserVars(req.config)) { + if (agent.tools?.some((t) => t.includes(Constants.mcp_delimiter))) { userMCPAuthMap = await getUserMCPAuthMap({ tools: agent.tools, userId: req.user.id, @@ -570,12 +592,17 @@ async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null, to const definitions = []; const allowedDomains = appConfig?.actions?.allowedDomains; - const domainSeparatorRegex = new RegExp(actionDomainSeparator, 'g'); + const normalizedToolNames = new Set( + actionToolNames.map((n) => n.replace(domainSeparatorRegex, '_')), + ); for (const action of actionSets) { const domain = await domainParser(action.metadata.domain, true); const normalizedDomain = domain.replace(domainSeparatorRegex, '_'); + const legacyDomain = legacyDomainEncode(action.metadata.domain); + const legacyNormalized = legacyDomain.replace(domainSeparatorRegex, '_'); + const isDomainAllowed = await isActionDomainAllowed(action.metadata.domain, allowedDomains); if (!isDomainAllowed) { logger.warn( @@ -595,7 +622,8 @@ async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null, to for (const sig of functionSignatures) { const toolName = `${sig.name}${actionDelimiter}${normalizedDomain}`; - if (!actionToolNames.some((name) => name.replace(domainSeparatorRegex, '_') === toolName)) { + const legacyToolName = `${sig.name}${actionDelimiter}${legacyNormalized}`; + if (!normalizedToolNames.has(toolName) && !normalizedToolNames.has(legacyToolName)) { continue; } @@ -766,6 +794,7 @@ async function loadToolDefinitionsWrapper({ req, res, agent, streamId = null, to toolContextMap, toolDefinitions, hasDeferredTools, + actionsEnabled, }; } @@ -809,14 +838,7 @@ async function loadAgentTools({ } const appConfig = req.config; - const endpointsConfig = await getEndpointsConfig(req); - let enabledCapabilities = new Set(endpointsConfig?.[EModelEndpoint.agents]?.capabilities ?? []); - /** Edge case: use defined/fallback capabilities when the "agents" endpoint is not enabled */ - if (enabledCapabilities.size === 0 && isEphemeralAgentId(agent.id)) { - enabledCapabilities = new Set( - appConfig.endpoints?.[EModelEndpoint.agents]?.capabilities ?? defaultAgentCapabilities, - ); - } + const enabledCapabilities = await resolveAgentCapabilities(req, appConfig, agent.id); const checkCapability = (capability) => { const enabled = enabledCapabilities.has(capability); if (!enabled) { @@ -833,6 +855,7 @@ async function loadAgentTools({ return enabled; }; const areToolsEnabled = checkCapability(AgentCapabilities.tools); + const actionsEnabled = checkCapability(AgentCapabilities.actions); let includesWebSearch = false; const _agentTools = agent.tools?.filter((tool) => { @@ -843,7 +866,9 @@ async function loadAgentTools({ } else if (tool === Tools.web_search) { includesWebSearch = checkCapability(AgentCapabilities.web_search); return includesWebSearch; - } else if (!areToolsEnabled && !tool.includes(actionDelimiter)) { + } else if (tool.includes(actionDelimiter)) { + return actionsEnabled; + } else if (!areToolsEnabled) { return false; } return true; @@ -860,8 +885,7 @@ async function loadAgentTools({ /** @type {Record>} */ let userMCPAuthMap; - //TODO pass config from registry - if (hasCustomUserVars(req.config)) { + if (agent.tools?.some((t) => t.includes(Constants.mcp_delimiter))) { userMCPAuthMap = await getUserMCPAuthMap({ tools: agent.tools, userId: req.user.id, @@ -949,13 +973,15 @@ async function loadAgentTools({ agentTools.push(...additionalTools); - if (!checkCapability(AgentCapabilities.actions)) { + const hasActionTools = _agentTools.some((t) => t.includes(actionDelimiter)); + if (!hasActionTools) { return { toolRegistry, userMCPAuthMap, toolContextMap, toolDefinitions, hasDeferredTools, + actionsEnabled, tools: agentTools, }; } @@ -971,19 +997,22 @@ async function loadAgentTools({ toolContextMap, toolDefinitions, hasDeferredTools, + actionsEnabled, tools: agentTools, }; } - // Process each action set once (validate spec, decrypt metadata) const processedActionSets = new Map(); - const domainMap = new Map(); + const domainLookupMap = new Map(); for (const action of actionSets) { const domain = await domainParser(action.metadata.domain, true); - domainMap.set(domain, action); + domainLookupMap.set(domain, domain); - // Check if domain is allowed (do this once per action set) + const legacyDomain = legacyDomainEncode(action.metadata.domain); + if (legacyDomain !== domain) { + domainLookupMap.set(legacyDomain, domain); + } const isDomainAllowed = await isActionDomainAllowed( action.metadata.domain, appConfig?.actions?.allowedDomains, @@ -1045,11 +1074,12 @@ async function loadAgentTools({ continue; } - // Find the matching domain for this tool let currentDomain = ''; - for (const domain of domainMap.keys()) { - if (toolName.includes(domain)) { - currentDomain = domain; + let matchedKey = ''; + for (const [key, canonical] of domainLookupMap.entries()) { + if (toolName.includes(key)) { + currentDomain = canonical; + matchedKey = key; break; } } @@ -1060,7 +1090,7 @@ async function loadAgentTools({ const { action, encrypted, zodSchemas, requestBuilders, functionSignatures } = processedActionSets.get(currentDomain); - const functionName = toolName.replace(`${actionDelimiter}${currentDomain}`, ''); + const functionName = toolName.replace(`${actionDelimiter}${matchedKey}`, ''); const functionSig = functionSignatures.find((sig) => sig.name === functionName); const requestBuilder = requestBuilders[functionName]; const zodSchema = zodSchemas[functionName]; @@ -1103,6 +1133,7 @@ async function loadAgentTools({ userMCPAuthMap, toolDefinitions, hasDeferredTools, + actionsEnabled, tools: agentTools, }; } @@ -1120,9 +1151,11 @@ async function loadAgentTools({ * @param {AbortSignal} [params.signal] - Abort signal * @param {Object} params.agent - The agent object * @param {string[]} params.toolNames - Names of tools to load + * @param {Map} [params.toolRegistry] - Tool registry * @param {Record>} [params.userMCPAuthMap] - User MCP auth map * @param {Object} [params.tool_resources] - Tool resources * @param {string|null} [params.streamId] - Stream ID for web search callbacks + * @param {boolean} [params.actionsEnabled] - Whether the actions capability is enabled * @returns {Promise<{ loadedTools: Array, configurable: Object }>} */ async function loadToolsForExecution({ @@ -1135,11 +1168,17 @@ async function loadToolsForExecution({ userMCPAuthMap, tool_resources, streamId = null, + actionsEnabled, }) { const appConfig = req.config; const allLoadedTools = []; const configurable = { userMCPAuthMap }; + if (actionsEnabled === undefined) { + const enabledCapabilities = await resolveAgentCapabilities(req, appConfig, agent?.id); + actionsEnabled = enabledCapabilities.has(AgentCapabilities.actions); + } + const isToolSearch = toolNames.includes(AgentConstants.TOOL_SEARCH); const isPTC = toolNames.includes(AgentConstants.PROGRAMMATIC_TOOL_CALLING); @@ -1196,7 +1235,6 @@ async function loadToolsForExecution({ const actionToolNames = allToolNamesToLoad.filter((name) => name.includes(actionDelimiter)); const regularToolNames = allToolNamesToLoad.filter((name) => !name.includes(actionDelimiter)); - /** @type {Record} */ if (regularToolNames.length > 0) { const includesWebSearch = regularToolNames.includes(Tools.web_search); const webSearchCallbacks = includesWebSearch ? createOnSearchResults(res, streamId) : undefined; @@ -1227,7 +1265,7 @@ async function loadToolsForExecution({ } } - if (actionToolNames.length > 0 && agent) { + if (actionToolNames.length > 0 && agent && actionsEnabled) { const actionTools = await loadActionToolsForExecution({ req, res, @@ -1237,6 +1275,11 @@ async function loadToolsForExecution({ actionToolNames, }); allLoadedTools.push(...actionTools); + } else if (actionToolNames.length > 0 && agent && !actionsEnabled) { + logger.warn( + `[loadToolsForExecution] Capability "${AgentCapabilities.actions}" disabled. ` + + `Skipping action tool execution. User: ${req.user.id} | Agent: ${agent.id} | Tools: ${actionToolNames.join(', ')}`, + ); } if (isPTC && allLoadedTools.length > 0) { @@ -1282,12 +1325,20 @@ async function loadActionToolsForExecution({ } const processedActionSets = new Map(); - const domainMap = new Map(); + /** Maps both new and legacy normalized domains to their canonical (new) domain key */ + const normalizedToDomain = new Map(); const allowedDomains = appConfig?.actions?.allowedDomains; for (const action of actionSets) { const domain = await domainParser(action.metadata.domain, true); - domainMap.set(domain, action); + const normalizedDomain = domain.replace(domainSeparatorRegex, '_'); + normalizedToDomain.set(normalizedDomain, domain); + + const legacyDomain = legacyDomainEncode(action.metadata.domain); + const legacyNormalized = legacyDomain.replace(domainSeparatorRegex, '_'); + if (legacyNormalized !== normalizedDomain) { + normalizedToDomain.set(legacyNormalized, domain); + } const isDomainAllowed = await isActionDomainAllowed(action.metadata.domain, allowedDomains); if (!isDomainAllowed) { @@ -1336,16 +1387,15 @@ async function loadActionToolsForExecution({ functionSignatures, zodSchemas, encrypted, + legacyNormalized, }); } - const domainSeparatorRegex = new RegExp(actionDomainSeparator, 'g'); for (const toolName of actionToolNames) { let currentDomain = ''; - for (const domain of domainMap.keys()) { - const normalizedDomain = domain.replace(domainSeparatorRegex, '_'); + for (const [normalizedDomain, canonicalDomain] of normalizedToDomain.entries()) { if (toolName.includes(normalizedDomain)) { - currentDomain = domain; + currentDomain = canonicalDomain; break; } } @@ -1354,7 +1404,7 @@ async function loadActionToolsForExecution({ continue; } - const { action, encrypted, zodSchemas, requestBuilders, functionSignatures } = + const { action, encrypted, zodSchemas, requestBuilders, functionSignatures, legacyNormalized } = processedActionSets.get(currentDomain); const normalizedDomain = currentDomain.replace(domainSeparatorRegex, '_'); const functionName = toolName.replace(`${actionDelimiter}${normalizedDomain}`, ''); @@ -1363,6 +1413,25 @@ async function loadActionToolsForExecution({ const zodSchema = zodSchemas[functionName]; if (!requestBuilder) { + const legacyFnName = toolName.replace(`${actionDelimiter}${legacyNormalized}`, ''); + if (legacyFnName !== toolName && requestBuilders[legacyFnName]) { + const legacyTool = await createActionTool({ + userId: req.user.id, + res, + action, + streamId, + encrypted, + requestBuilder: requestBuilders[legacyFnName], + zodSchema: zodSchemas[legacyFnName], + name: toolName, + description: + functionSignatures.find((sig) => sig.name === legacyFnName)?.description ?? '', + useSSRFProtection: !Array.isArray(allowedDomains) || allowedDomains.length === 0, + }); + if (legacyTool) { + loadedActionTools.push(legacyTool); + } + } continue; } @@ -1397,4 +1466,5 @@ module.exports = { loadAgentTools, loadToolsForExecution, processRequiredActions, + resolveAgentCapabilities, }; diff --git a/api/server/services/Tools/mcp.js b/api/server/services/Tools/mcp.js index 10f2d71a18..7589043e10 100644 --- a/api/server/services/Tools/mcp.js +++ b/api/server/services/Tools/mcp.js @@ -1,8 +1,8 @@ const { logger } = require('@librechat/data-schemas'); const { CacheKeys, Constants } = require('librechat-data-provider'); +const { getMCPManager, getMCPServersRegistry, getFlowStateManager } = require('~/config'); const { findToken, createToken, updateToken, deleteTokens } = require('~/models'); const { updateMCPServerTools } = require('~/server/services/Config'); -const { getMCPManager, getFlowStateManager } = require('~/config'); const { getLogStores } = require('~/cache'); /** @@ -41,6 +41,33 @@ async function reinitMCPServer({ let oauthUrl = null; try { + const registry = getMCPServersRegistry(); + const serverConfig = await registry.getServerConfig(serverName, user?.id); + if (serverConfig?.inspectionFailed) { + logger.info( + `[MCP Reinitialize] Server ${serverName} had failed inspection, attempting reinspection`, + ); + try { + const storageLocation = serverConfig.dbId ? 'DB' : 'CACHE'; + await registry.reinspectServer(serverName, storageLocation, user?.id); + logger.info(`[MCP Reinitialize] Reinspection succeeded for server: ${serverName}`); + } catch (reinspectError) { + logger.error( + `[MCP Reinitialize] Reinspection failed for server ${serverName}:`, + reinspectError, + ); + return { + availableTools: null, + success: false, + message: `MCP server '${serverName}' is still unreachable`, + oauthRequired: false, + serverName, + oauthUrl: null, + tools: null, + }; + } + } + const customUserVars = userMCPAuthMap?.[`${Constants.mcp_prefix}${serverName}`]; const flowManager = _flowManager ?? getFlowStateManager(getLogStores(CacheKeys.FLOWS)); const mcpManager = getMCPManager(); diff --git a/api/server/services/__tests__/ToolService.spec.js b/api/server/services/__tests__/ToolService.spec.js index 2f00bbc3d6..a468a88eb3 100644 --- a/api/server/services/__tests__/ToolService.spec.js +++ b/api/server/services/__tests__/ToolService.spec.js @@ -1,15 +1,304 @@ -const { AgentCapabilities, defaultAgentCapabilities } = require('librechat-data-provider'); +const { + Tools, + Constants, + EModelEndpoint, + actionDelimiter, + AgentCapabilities, + defaultAgentCapabilities, +} = require('librechat-data-provider'); + +const mockGetEndpointsConfig = jest.fn(); +const mockGetMCPServerTools = jest.fn(); +const mockGetCachedTools = jest.fn(); +jest.mock('~/server/services/Config', () => ({ + getEndpointsConfig: (...args) => mockGetEndpointsConfig(...args), + getMCPServerTools: (...args) => mockGetMCPServerTools(...args), + getCachedTools: (...args) => mockGetCachedTools(...args), +})); + +const mockLoadToolDefinitions = jest.fn(); +const mockGetUserMCPAuthMap = jest.fn(); +jest.mock('@librechat/api', () => ({ + ...jest.requireActual('@librechat/api'), + loadToolDefinitions: (...args) => mockLoadToolDefinitions(...args), + getUserMCPAuthMap: (...args) => mockGetUserMCPAuthMap(...args), +})); + +const mockLoadToolsUtil = jest.fn(); +jest.mock('~/app/clients/tools/util', () => ({ + loadTools: (...args) => mockLoadToolsUtil(...args), +})); + +const mockLoadActionSets = jest.fn(); +jest.mock('~/server/services/Tools/credentials', () => ({ + loadAuthValues: jest.fn().mockResolvedValue({}), +})); +jest.mock('~/server/services/Tools/search', () => ({ + createOnSearchResults: jest.fn(), +})); +jest.mock('~/server/services/Tools/mcp', () => ({ + reinitMCPServer: jest.fn(), +})); +jest.mock('~/server/services/Files/process', () => ({ + processFileURL: jest.fn(), + uploadImageBuffer: jest.fn(), +})); +jest.mock('~/app/clients/tools/util/fileSearch', () => ({ + primeFiles: jest.fn().mockResolvedValue({}), +})); +jest.mock('~/server/services/Files/Code/process', () => ({ + primeFiles: jest.fn().mockResolvedValue({}), +})); +jest.mock('../ActionService', () => ({ + loadActionSets: (...args) => mockLoadActionSets(...args), + decryptMetadata: jest.fn(), + createActionTool: jest.fn(), + domainParser: jest.fn(), +})); +jest.mock('~/server/services/Threads', () => ({ + recordUsage: jest.fn(), +})); +jest.mock('~/models', () => ({ + findPluginAuthsByKeys: jest.fn(), +})); +jest.mock('~/config', () => ({ + getFlowStateManager: jest.fn(() => ({})), +})); +jest.mock('~/cache', () => ({ + getLogStores: jest.fn(() => ({})), +})); + +const { + loadAgentTools, + loadToolsForExecution, + resolveAgentCapabilities, +} = require('../ToolService'); + +function createMockReq(capabilities) { + return { + user: { id: 'user_123' }, + config: { + endpoints: { + [EModelEndpoint.agents]: { + capabilities, + }, + }, + }, + }; +} + +function createEndpointsConfig(capabilities) { + return { + [EModelEndpoint.agents]: { capabilities }, + }; +} + +describe('ToolService - Action Capability Gating', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockLoadToolDefinitions.mockResolvedValue({ + toolDefinitions: [], + toolRegistry: new Map(), + hasDeferredTools: false, + }); + mockLoadToolsUtil.mockResolvedValue({ loadedTools: [], toolContextMap: {} }); + mockLoadActionSets.mockResolvedValue([]); + }); + + describe('resolveAgentCapabilities', () => { + it('should return capabilities from endpoints config', async () => { + const capabilities = [AgentCapabilities.tools, AgentCapabilities.actions]; + const req = createMockReq(capabilities); + mockGetEndpointsConfig.mockResolvedValue(createEndpointsConfig(capabilities)); + + const result = await resolveAgentCapabilities(req, req.config, 'agent_123'); + + expect(result).toBeInstanceOf(Set); + expect(result.has(AgentCapabilities.tools)).toBe(true); + expect(result.has(AgentCapabilities.actions)).toBe(true); + expect(result.has(AgentCapabilities.web_search)).toBe(false); + }); + + it('should fall back to default capabilities for ephemeral agents with empty config', async () => { + const req = createMockReq(defaultAgentCapabilities); + mockGetEndpointsConfig.mockResolvedValue({}); + + const result = await resolveAgentCapabilities(req, req.config, Constants.EPHEMERAL_AGENT_ID); + + for (const cap of defaultAgentCapabilities) { + expect(result.has(cap)).toBe(true); + } + }); + + it('should return empty set when no capabilities and not ephemeral', async () => { + const req = createMockReq([]); + mockGetEndpointsConfig.mockResolvedValue({}); + + const result = await resolveAgentCapabilities(req, req.config, 'agent_123'); + + expect(result.size).toBe(0); + }); + }); + + describe('loadAgentTools (definitionsOnly=true) — action tool filtering', () => { + const actionToolName = `get_weather${actionDelimiter}api_example_com`; + const regularTool = 'calculator'; + + it('should exclude action tools from definitions when actions capability is disabled', async () => { + const capabilities = [AgentCapabilities.tools, AgentCapabilities.web_search]; + const req = createMockReq(capabilities); + mockGetEndpointsConfig.mockResolvedValue(createEndpointsConfig(capabilities)); + + await loadAgentTools({ + req, + res: {}, + agent: { id: 'agent_123', tools: [regularTool, actionToolName] }, + definitionsOnly: true, + }); + + expect(mockLoadToolDefinitions).toHaveBeenCalledTimes(1); + const [callArgs] = mockLoadToolDefinitions.mock.calls[0]; + expect(callArgs.tools).toContain(regularTool); + expect(callArgs.tools).not.toContain(actionToolName); + }); + + it('should include action tools in definitions when actions capability is enabled', async () => { + const capabilities = [AgentCapabilities.tools, AgentCapabilities.actions]; + const req = createMockReq(capabilities); + mockGetEndpointsConfig.mockResolvedValue(createEndpointsConfig(capabilities)); + + await loadAgentTools({ + req, + res: {}, + agent: { id: 'agent_123', tools: [regularTool, actionToolName] }, + definitionsOnly: true, + }); + + expect(mockLoadToolDefinitions).toHaveBeenCalledTimes(1); + const [callArgs] = mockLoadToolDefinitions.mock.calls[0]; + expect(callArgs.tools).toContain(regularTool); + expect(callArgs.tools).toContain(actionToolName); + }); + + it('should return actionsEnabled in the result', async () => { + const capabilities = [AgentCapabilities.tools]; + const req = createMockReq(capabilities); + mockGetEndpointsConfig.mockResolvedValue(createEndpointsConfig(capabilities)); + + const result = await loadAgentTools({ + req, + res: {}, + agent: { id: 'agent_123', tools: [regularTool] }, + definitionsOnly: true, + }); + + expect(result.actionsEnabled).toBe(false); + }); + }); + + describe('loadAgentTools (definitionsOnly=false) — action tool filtering', () => { + const actionToolName = `get_weather${actionDelimiter}api_example_com`; + const regularTool = 'calculator'; + + it('should not load action sets when actions capability is disabled', async () => { + const capabilities = [AgentCapabilities.tools, AgentCapabilities.web_search]; + const req = createMockReq(capabilities); + mockGetEndpointsConfig.mockResolvedValue(createEndpointsConfig(capabilities)); + + await loadAgentTools({ + req, + res: {}, + agent: { id: 'agent_123', tools: [regularTool, actionToolName] }, + definitionsOnly: false, + }); + + expect(mockLoadActionSets).not.toHaveBeenCalled(); + }); + + it('should load action sets when actions capability is enabled and action tools present', async () => { + const capabilities = [AgentCapabilities.tools, AgentCapabilities.actions]; + const req = createMockReq(capabilities); + mockGetEndpointsConfig.mockResolvedValue(createEndpointsConfig(capabilities)); + + await loadAgentTools({ + req, + res: {}, + agent: { id: 'agent_123', tools: [regularTool, actionToolName] }, + definitionsOnly: false, + }); + + expect(mockLoadActionSets).toHaveBeenCalledWith({ agent_id: 'agent_123' }); + }); + }); + + describe('loadToolsForExecution — action tool gating', () => { + const actionToolName = `get_weather${actionDelimiter}api_example_com`; + const regularTool = Tools.web_search; + + it('should skip action tool loading when actionsEnabled=false', async () => { + const req = createMockReq([]); + req.config = {}; + + const result = await loadToolsForExecution({ + req, + res: {}, + agent: { id: 'agent_123' }, + toolNames: [regularTool, actionToolName], + actionsEnabled: false, + }); + + expect(mockLoadActionSets).not.toHaveBeenCalled(); + expect(result.loadedTools).toBeDefined(); + }); + + it('should load action tools when actionsEnabled=true', async () => { + const req = createMockReq([AgentCapabilities.actions]); + req.config = {}; + + await loadToolsForExecution({ + req, + res: {}, + agent: { id: 'agent_123' }, + toolNames: [actionToolName], + actionsEnabled: true, + }); + + expect(mockLoadActionSets).toHaveBeenCalledWith({ agent_id: 'agent_123' }); + }); + + it('should resolve actionsEnabled from capabilities when not explicitly provided', async () => { + const capabilities = [AgentCapabilities.tools]; + const req = createMockReq(capabilities); + mockGetEndpointsConfig.mockResolvedValue(createEndpointsConfig(capabilities)); + + await loadToolsForExecution({ + req, + res: {}, + agent: { id: 'agent_123' }, + toolNames: [actionToolName], + }); + + expect(mockGetEndpointsConfig).toHaveBeenCalled(); + expect(mockLoadActionSets).not.toHaveBeenCalled(); + }); + + it('should not call loadActionSets when there are no action tools', async () => { + const req = createMockReq([AgentCapabilities.actions]); + req.config = {}; + + await loadToolsForExecution({ + req, + res: {}, + agent: { id: 'agent_123' }, + toolNames: [regularTool], + actionsEnabled: true, + }); + + expect(mockLoadActionSets).not.toHaveBeenCalled(); + }); + }); -/** - * Tests for ToolService capability checking logic. - * The actual loadAgentTools function has many dependencies, so we test - * the capability checking logic in isolation. - */ -describe('ToolService - Capability Checking', () => { describe('checkCapability logic', () => { - /** - * Simulates the checkCapability function from loadAgentTools - */ const createCheckCapability = (enabledCapabilities, logger = { warn: jest.fn() }) => { return (capability) => { const enabled = enabledCapabilities.has(capability); @@ -119,26 +408,68 @@ describe('ToolService - Capability Checking', () => { }); }); + describe('userMCPAuthMap gating', () => { + const shouldFetchMCPAuth = (tools) => + tools?.some((t) => t.includes(Constants.mcp_delimiter)) ?? false; + + it('should return true when agent has MCP tools', () => { + const tools = ['web_search', `search${Constants.mcp_delimiter}my-mcp-server`, 'calculator']; + expect(shouldFetchMCPAuth(tools)).toBe(true); + }); + + it('should return false when agent has no MCP tools', () => { + const tools = ['web_search', 'calculator', 'code_interpreter']; + expect(shouldFetchMCPAuth(tools)).toBe(false); + }); + + it('should return false when tools is empty', () => { + expect(shouldFetchMCPAuth([])).toBe(false); + }); + + it('should return false when tools is undefined', () => { + expect(shouldFetchMCPAuth(undefined)).toBe(false); + }); + + it('should return false when tools is null', () => { + expect(shouldFetchMCPAuth(null)).toBe(false); + }); + + it('should detect MCP tools with different server names', () => { + const tools = [ + `listFiles${Constants.mcp_delimiter}file-server`, + `query${Constants.mcp_delimiter}db-server`, + ]; + expect(shouldFetchMCPAuth(tools)).toBe(true); + }); + + it('should return true even when only one tool is MCP', () => { + const tools = [ + 'web_search', + 'calculator', + 'code_interpreter', + `echo${Constants.mcp_delimiter}test-server`, + ]; + expect(shouldFetchMCPAuth(tools)).toBe(true); + }); + }); + describe('deferredToolsEnabled integration', () => { it('should correctly determine deferredToolsEnabled from capabilities set', () => { const createCheckCapability = (enabledCapabilities) => { return (capability) => enabledCapabilities.has(capability); }; - // When deferred_tools is in capabilities const withDeferred = new Set([AgentCapabilities.deferred_tools, AgentCapabilities.tools]); const checkWithDeferred = createCheckCapability(withDeferred); expect(checkWithDeferred(AgentCapabilities.deferred_tools)).toBe(true); - // When deferred_tools is NOT in capabilities const withoutDeferred = new Set([AgentCapabilities.tools, AgentCapabilities.actions]); const checkWithoutDeferred = createCheckCapability(withoutDeferred); expect(checkWithoutDeferred(AgentCapabilities.deferred_tools)).toBe(false); }); it('should use defaultAgentCapabilities when no capabilities configured', () => { - // Simulates the fallback behavior in loadAgentTools - const endpointsConfig = {}; // No capabilities configured + const endpointsConfig = {}; const enabledCapabilities = new Set( endpointsConfig?.capabilities ?? defaultAgentCapabilities, ); diff --git a/api/server/services/cleanup.js b/api/server/services/cleanup.js index 7d3dfdec12..dc4f62c2ac 100644 --- a/api/server/services/cleanup.js +++ b/api/server/services/cleanup.js @@ -1,5 +1,5 @@ const { logger } = require('@librechat/data-schemas'); -const { deleteNullOrEmptyConversations } = require('~/models/Conversation'); +const { deleteNullOrEmptyConversations } = require('~/models'); const cleanup = async () => { try { diff --git a/api/server/services/initializeMCPs.spec.js b/api/server/services/initializeMCPs.spec.js index e37e12c356..d72fda0e00 100644 --- a/api/server/services/initializeMCPs.spec.js +++ b/api/server/services/initializeMCPs.spec.js @@ -3,8 +3,8 @@ * * These tests verify that MCPServersRegistry and MCPManager are ALWAYS initialized, * even when no explicitly configured MCP servers exist. This is critical for the - * "Dynamic MCP Server Management" feature (v0.8.2-rc1) which allows users to - * add MCP servers via the UI without requiring explicit configuration. + * "Dynamic MCP Server Management" feature (introduced in `0.8.2-rc1` release) which + * allows users to add MCP servers via the UI without requiring explicit configuration. * * Bug fixed: Previously, MCPManager was only initialized when mcpServers existed * in librechat.yaml, causing "MCPManager has not been initialized" errors when diff --git a/api/server/services/start/migration.js b/api/server/services/start/migration.js index 83b9c83e39..70f8300e08 100644 --- a/api/server/services/start/migration.js +++ b/api/server/services/start/migration.js @@ -6,8 +6,6 @@ const { checkAgentPermissionsMigration, checkPromptPermissionsMigration, } = require('@librechat/api'); -const { getProjectByName } = require('~/models/Project'); -const { Agent, PromptGroup } = require('~/db/models'); const { findRoleByIdentifier } = require('~/models'); /** @@ -20,9 +18,8 @@ async function checkMigrations() { mongoose, methods: { findRoleByIdentifier, - getProjectByName, }, - AgentModel: Agent, + AgentModel: mongoose.models.Agent, }); logAgentMigrationWarning(agentMigrationResult); } catch (error) { @@ -33,9 +30,8 @@ async function checkMigrations() { mongoose, methods: { findRoleByIdentifier, - getProjectByName, }, - PromptGroupModel: PromptGroup, + PromptGroupModel: mongoose.models.PromptGroup, }); logPromptMigrationWarning(promptMigrationResult); } catch (error) { diff --git a/api/server/services/systemGrant.spec.js b/api/server/services/systemGrant.spec.js new file mode 100644 index 0000000000..4e10ee5641 --- /dev/null +++ b/api/server/services/systemGrant.spec.js @@ -0,0 +1,407 @@ +const mongoose = require('mongoose'); +const { createModels, createMethods } = require('@librechat/data-schemas'); +const { MongoMemoryServer } = require('mongodb-memory-server'); +const { SystemRoles, PrincipalType } = require('librechat-data-provider'); +const { SystemCapabilities } = require('@librechat/data-schemas'); + +jest.mock('@librechat/data-schemas', () => ({ + ...jest.requireActual('@librechat/data-schemas'), + getTransactionSupport: jest.fn().mockResolvedValue(false), + createModels: jest.requireActual('@librechat/data-schemas').createModels, + createMethods: jest.requireActual('@librechat/data-schemas').createMethods, +})); + +jest.mock('~/server/services/GraphApiService', () => ({ + entraIdPrincipalFeatureEnabled: jest.fn().mockReturnValue(false), + getUserOwnedEntraGroups: jest.fn().mockResolvedValue([]), + getUserEntraGroups: jest.fn().mockResolvedValue([]), + getGroupMembers: jest.fn().mockResolvedValue([]), + getGroupOwners: jest.fn().mockResolvedValue([]), +})); + +jest.mock('~/config', () => ({ + logger: { error: jest.fn() }, +})); + +let mongoServer; +let methods; +let SystemGrant; + +beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + await mongoose.connect(mongoServer.getUri()); + + createModels(mongoose); + const dbModels = require('~/db/models'); + Object.assign(mongoose.models, dbModels); + SystemGrant = dbModels.SystemGrant; + + methods = createMethods(mongoose, { + matchModelName: () => null, + findMatchingPattern: () => null, + getCache: () => ({ + get: async () => null, + set: async () => {}, + }), + }); +}); + +afterAll(async () => { + await mongoose.disconnect(); + await mongoServer.stop(); +}); + +beforeEach(async () => { + await SystemGrant.deleteMany({}); +}); + +describe('SystemGrant methods', () => { + describe('seedSystemGrants', () => { + it('seeds all capabilities for the ADMIN role', async () => { + await methods.seedSystemGrants(); + + const grants = await SystemGrant.find({ + principalType: PrincipalType.ROLE, + principalId: SystemRoles.ADMIN, + }).lean(); + + const expectedCount = Object.values(SystemCapabilities).length; + expect(grants).toHaveLength(expectedCount); + + const capabilities = grants.map((g) => g.capability).sort(); + const expected = Object.values(SystemCapabilities).sort(); + expect(capabilities).toEqual(expected); + }); + + it('is idempotent — calling twice does not duplicate grants', async () => { + await methods.seedSystemGrants(); + await methods.seedSystemGrants(); + + const count = await SystemGrant.countDocuments({ + principalType: PrincipalType.ROLE, + principalId: SystemRoles.ADMIN, + }); + + expect(count).toBe(Object.values(SystemCapabilities).length); + }); + + it('seeds grants with no tenantId', async () => { + await methods.seedSystemGrants(); + + const withTenant = await SystemGrant.countDocuments({ + principalType: PrincipalType.ROLE, + principalId: SystemRoles.ADMIN, + tenantId: { $exists: true }, + }); + + expect(withTenant).toBe(0); + }); + }); + + describe('grantCapability / revokeCapability', () => { + it('grants a capability to a user', async () => { + const userId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_USERS, + }); + + const grant = await SystemGrant.findOne({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_USERS, + }).lean(); + + expect(grant).toBeTruthy(); + expect(grant.grantedAt).toBeInstanceOf(Date); + }); + + it('upsert does not create duplicates', async () => { + const userId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_USERS, + }); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_USERS, + }); + + const count = await SystemGrant.countDocuments({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_USERS, + }); + + expect(count).toBe(1); + }); + + it('revokes a capability', async () => { + const userId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_USERS, + }); + + await methods.revokeCapability({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_USERS, + }); + + const grant = await SystemGrant.findOne({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_USERS, + }).lean(); + + expect(grant).toBeNull(); + }); + }); + + describe('hasCapabilityForPrincipals', () => { + it('returns true when role principal has the capability', async () => { + await methods.seedSystemGrants(); + + const principals = [ + { principalType: PrincipalType.USER, principalId: new mongoose.Types.ObjectId() }, + { principalType: PrincipalType.ROLE, principalId: SystemRoles.ADMIN }, + { principalType: PrincipalType.PUBLIC }, + ]; + + const result = await methods.hasCapabilityForPrincipals({ + principals, + capability: SystemCapabilities.ACCESS_ADMIN, + }); + + expect(result).toBe(true); + }); + + it('returns false when no principal has the capability', async () => { + const principals = [ + { principalType: PrincipalType.USER, principalId: new mongoose.Types.ObjectId() }, + { principalType: PrincipalType.ROLE, principalId: SystemRoles.USER }, + { principalType: PrincipalType.PUBLIC }, + ]; + + const result = await methods.hasCapabilityForPrincipals({ + principals, + capability: SystemCapabilities.ACCESS_ADMIN, + }); + + expect(result).toBe(false); + }); + + it('returns false for an empty principals list', async () => { + const result = await methods.hasCapabilityForPrincipals({ + principals: [], + capability: SystemCapabilities.ACCESS_ADMIN, + }); + + expect(result).toBe(false); + }); + + it('ignores PUBLIC principals', async () => { + const result = await methods.hasCapabilityForPrincipals({ + principals: [{ principalType: PrincipalType.PUBLIC }], + capability: SystemCapabilities.ACCESS_ADMIN, + }); + + expect(result).toBe(false); + }); + + it('matches user-level grants', async () => { + const userId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_CONFIGS, + }); + + const principals = [ + { principalType: PrincipalType.USER, principalId: userId }, + { principalType: PrincipalType.ROLE, principalId: SystemRoles.USER }, + { principalType: PrincipalType.PUBLIC }, + ]; + + const result = await methods.hasCapabilityForPrincipals({ + principals, + capability: SystemCapabilities.READ_CONFIGS, + }); + + expect(result).toBe(true); + }); + + it('matches group-level grants', async () => { + const groupId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.GROUP, + principalId: groupId, + capability: SystemCapabilities.READ_USAGE, + }); + + const principals = [ + { principalType: PrincipalType.USER, principalId: new mongoose.Types.ObjectId() }, + { principalType: PrincipalType.GROUP, principalId: groupId }, + { principalType: PrincipalType.PUBLIC }, + ]; + + const result = await methods.hasCapabilityForPrincipals({ + principals, + capability: SystemCapabilities.READ_USAGE, + }); + + expect(result).toBe(true); + }); + }); + + describe('getCapabilitiesForPrincipal', () => { + it('lists all capabilities for a principal', async () => { + await methods.seedSystemGrants(); + + const grants = await methods.getCapabilitiesForPrincipal({ + principalType: PrincipalType.ROLE, + principalId: SystemRoles.ADMIN, + }); + + expect(grants).toHaveLength(Object.values(SystemCapabilities).length); + }); + + it('returns empty array for a principal with no grants', async () => { + const grants = await methods.getCapabilitiesForPrincipal({ + principalType: PrincipalType.ROLE, + principalId: SystemRoles.USER, + }); + + expect(grants).toHaveLength(0); + }); + }); + + describe('principalId normalization', () => { + it('grant with string userId is found by hasCapabilityForPrincipals with ObjectId', async () => { + const userId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId.toString(), // string input + capability: SystemCapabilities.READ_USAGE, + }); + + const result = await methods.hasCapabilityForPrincipals({ + principals: [{ principalType: PrincipalType.USER, principalId: userId }], // ObjectId input + capability: SystemCapabilities.READ_USAGE, + }); + + expect(result).toBe(true); + }); + + it('revoke with string userId removes the grant stored as ObjectId', async () => { + const userId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId.toString(), + capability: SystemCapabilities.READ_USAGE, + }); + + await methods.revokeCapability({ + principalType: PrincipalType.USER, + principalId: userId.toString(), // string revoke + capability: SystemCapabilities.READ_USAGE, + }); + + const result = await methods.hasCapabilityForPrincipals({ + principals: [{ principalType: PrincipalType.USER, principalId: userId }], + capability: SystemCapabilities.READ_USAGE, + }); + + expect(result).toBe(false); + }); + + it('getCapabilitiesForPrincipal with string userId returns grants stored as ObjectId', async () => { + const userId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId.toString(), + capability: SystemCapabilities.READ_USAGE, + }); + + const grants = await methods.getCapabilitiesForPrincipal({ + principalType: PrincipalType.USER, + principalId: userId.toString(), // string lookup + }); + + expect(grants).toHaveLength(1); + expect(grants[0].capability).toBe(SystemCapabilities.READ_USAGE); + }); + }); + + describe('tenant scoping', () => { + it('tenant-scoped grant does not match platform-level query', async () => { + const userId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_CONFIGS, + tenantId: 'tenant-1', + }); + + const result = await methods.hasCapabilityForPrincipals({ + principals: [{ principalType: PrincipalType.USER, principalId: userId }], + capability: SystemCapabilities.READ_CONFIGS, + }); + + expect(result).toBe(false); + }); + + it('tenant-scoped grant matches same-tenant query', async () => { + const userId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_CONFIGS, + tenantId: 'tenant-1', + }); + + const result = await methods.hasCapabilityForPrincipals({ + principals: [{ principalType: PrincipalType.USER, principalId: userId }], + capability: SystemCapabilities.READ_CONFIGS, + tenantId: 'tenant-1', + }); + + expect(result).toBe(true); + }); + + it('tenant-scoped grant does not match different tenant', async () => { + const userId = new mongoose.Types.ObjectId(); + + await methods.grantCapability({ + principalType: PrincipalType.USER, + principalId: userId, + capability: SystemCapabilities.READ_CONFIGS, + tenantId: 'tenant-1', + }); + + const result = await methods.hasCapabilityForPrincipals({ + principals: [{ principalType: PrincipalType.USER, principalId: userId }], + capability: SystemCapabilities.READ_CONFIGS, + tenantId: 'tenant-2', + }); + + expect(result).toBe(false); + }); + }); +}); diff --git a/api/server/services/twoFactorService.js b/api/server/services/twoFactorService.js index cce24e2322..313c557133 100644 --- a/api/server/services/twoFactorService.js +++ b/api/server/services/twoFactorService.js @@ -153,9 +153,11 @@ const generateBackupCodes = async (count = 10) => { * @param {Object} params * @param {Object} params.user * @param {string} params.backupCode + * @param {boolean} [params.persist=true] - Whether to persist the used-mark to the database. + * Pass `false` when the caller will immediately overwrite `backupCodes` (e.g. re-enrollment). * @returns {Promise} */ -const verifyBackupCode = async ({ user, backupCode }) => { +const verifyBackupCode = async ({ user, backupCode, persist = true }) => { if (!backupCode || !user || !Array.isArray(user.backupCodes)) { return false; } @@ -165,17 +167,50 @@ const verifyBackupCode = async ({ user, backupCode }) => { (codeObj) => codeObj.codeHash === hashedInput && !codeObj.used, ); - if (matchingCode) { + if (!matchingCode) { + return false; + } + + if (persist) { const updatedBackupCodes = user.backupCodes.map((codeObj) => codeObj.codeHash === hashedInput && !codeObj.used ? { ...codeObj, used: true, usedAt: new Date() } : codeObj, ); - // Update the user record with the marked backup code. await updateUser(user._id, { backupCodes: updatedBackupCodes }); - return true; } - return false; + return true; +}; + +/** + * Verifies a user's identity via TOTP token or backup code. + * @param {Object} params + * @param {Object} params.user - The user document (must include totpSecret and backupCodes). + * @param {string} [params.token] - A 6-digit TOTP token. + * @param {string} [params.backupCode] - An 8-character backup code. + * @param {boolean} [params.persistBackupUse=true] - Whether to mark the backup code as used in the DB. + * @returns {Promise<{ verified: boolean, status?: number, message?: string }>} + */ +const verifyOTPOrBackupCode = async ({ user, token, backupCode, persistBackupUse = true }) => { + if (!token && !backupCode) { + return { verified: false, status: 400 }; + } + + if (token) { + const secret = await getTOTPSecret(user.totpSecret); + if (!secret) { + return { verified: false, status: 400, message: '2FA secret is missing or corrupted' }; + } + const ok = await verifyTOTP(secret, token); + return ok + ? { verified: true } + : { verified: false, status: 401, message: 'Invalid token or backup code' }; + } + + const ok = await verifyBackupCode({ user, backupCode, persist: persistBackupUse }); + return ok + ? { verified: true } + : { verified: false, status: 401, message: 'Invalid token or backup code' }; }; /** @@ -213,11 +248,12 @@ const generate2FATempToken = (userId) => { }; module.exports = { - generateTOTPSecret, - generateTOTP, - verifyTOTP, + verifyOTPOrBackupCode, + generate2FATempToken, generateBackupCodes, + generateTOTPSecret, verifyBackupCode, getTOTPSecret, - generate2FATempToken, + generateTOTP, + verifyTOTP, }; diff --git a/api/server/utils/import/fork.js b/api/server/utils/import/fork.js index c4ce8cb5d4..5df4d27af2 100644 --- a/api/server/utils/import/fork.js +++ b/api/server/utils/import/fork.js @@ -3,8 +3,7 @@ const { logger } = require('@librechat/data-schemas'); const { EModelEndpoint, Constants, ForkOptions } = require('librechat-data-provider'); const { createImportBatchBuilder } = require('./importBatchBuilder'); const BaseClient = require('~/app/clients/BaseClient'); -const { getConvo } = require('~/models/Conversation'); -const { getMessages } = require('~/models/Message'); +const { getConvo, getMessages } = require('~/models'); /** * Helper function to clone messages with proper parent-child relationships and timestamps @@ -358,16 +357,15 @@ function splitAtTargetLevel(messages, targetMessageId) { * @param {object} params - The parameters for duplicating the conversation. * @param {string} params.userId - The ID of the user duplicating the conversation. * @param {string} params.conversationId - The ID of the conversation to duplicate. + * @param {string} [params.title] - Optional title override for the duplicate. * @returns {Promise<{ conversation: TConversation, messages: TMessage[] }>} The duplicated conversation and messages. */ -async function duplicateConversation({ userId, conversationId }) { - // Get original conversation +async function duplicateConversation({ userId, conversationId, title }) { const originalConvo = await getConvo(userId, conversationId); if (!originalConvo) { throw new Error('Conversation not found'); } - // Get original messages const originalMessages = await getMessages({ user: userId, conversationId, @@ -383,14 +381,11 @@ async function duplicateConversation({ userId, conversationId }) { cloneMessagesWithTimestamps(messagesToClone, importBatchBuilder); - const result = importBatchBuilder.finishConversation( - originalConvo.title, - new Date(), - originalConvo, - ); + const duplicateTitle = title || originalConvo.title; + const result = importBatchBuilder.finishConversation(duplicateTitle, new Date(), originalConvo); await importBatchBuilder.saveBatch(); logger.debug( - `user: ${userId} | New conversation "${originalConvo.title}" duplicated from conversation ID ${conversationId}`, + `user: ${userId} | New conversation "${duplicateTitle}" duplicated from conversation ID ${conversationId}`, ); const conversation = await getConvo(userId, result.conversation.conversationId); diff --git a/api/server/utils/import/fork.spec.js b/api/server/utils/import/fork.spec.js index 552620dc89..6fd108674a 100644 --- a/api/server/utils/import/fork.spec.js +++ b/api/server/utils/import/fork.spec.js @@ -1,16 +1,10 @@ const { Constants, ForkOptions } = require('librechat-data-provider'); -jest.mock('~/models/Conversation', () => ({ +jest.mock('~/models', () => ({ getConvo: jest.fn(), bulkSaveConvos: jest.fn(), -})); - -jest.mock('~/models/Message', () => ({ getMessages: jest.fn(), bulkSaveMessages: jest.fn(), -})); - -jest.mock('~/models/ConversationTag', () => ({ bulkIncrementTagCounts: jest.fn(), })); @@ -32,9 +26,13 @@ const { getMessagesUpToTargetLevel, cloneMessagesWithTimestamps, } = require('./fork'); -const { bulkIncrementTagCounts } = require('~/models/ConversationTag'); -const { getConvo, bulkSaveConvos } = require('~/models/Conversation'); -const { getMessages, bulkSaveMessages } = require('~/models/Message'); +const { + bulkIncrementTagCounts, + getConvo, + bulkSaveConvos, + getMessages, + bulkSaveMessages, +} = require('~/models'); const { createImportBatchBuilder } = require('./importBatchBuilder'); const BaseClient = require('~/app/clients/BaseClient'); diff --git a/api/server/utils/import/importBatchBuilder.js b/api/server/utils/import/importBatchBuilder.js index 5e499043d2..29fbfa85a2 100644 --- a/api/server/utils/import/importBatchBuilder.js +++ b/api/server/utils/import/importBatchBuilder.js @@ -1,9 +1,7 @@ const { v4: uuidv4 } = require('uuid'); const { logger } = require('@librechat/data-schemas'); const { EModelEndpoint, Constants, openAISettings } = require('librechat-data-provider'); -const { bulkIncrementTagCounts } = require('~/models/ConversationTag'); -const { bulkSaveConvos } = require('~/models/Conversation'); -const { bulkSaveMessages } = require('~/models/Message'); +const { bulkIncrementTagCounts, bulkSaveConvos, bulkSaveMessages } = require('~/models'); /** * Factory function for creating an instance of ImportBatchBuilder. diff --git a/api/server/utils/import/importConversations.js b/api/server/utils/import/importConversations.js index d9e4d4332d..e56176c609 100644 --- a/api/server/utils/import/importConversations.js +++ b/api/server/utils/import/importConversations.js @@ -1,7 +1,10 @@ const fs = require('fs').promises; +const { resolveImportMaxFileSize } = require('@librechat/api'); const { logger } = require('@librechat/data-schemas'); const { getImporter } = require('./importers'); +const maxFileSize = resolveImportMaxFileSize(); + /** * Job definition for importing a conversation. * @param {{ filepath, requestUserId }} job - The job object. @@ -11,11 +14,10 @@ const importConversations = async (job) => { try { logger.debug(`user: ${requestUserId} | Importing conversation(s) from file...`); - /* error if file is too large */ const fileInfo = await fs.stat(filepath); - if (fileInfo.size > process.env.CONVERSATION_IMPORT_MAX_FILE_SIZE_BYTES) { + if (fileInfo.size > maxFileSize) { throw new Error( - `File size is ${fileInfo.size} bytes. It exceeds the maximum limit of ${process.env.CONVERSATION_IMPORT_MAX_FILE_SIZE_BYTES} bytes.`, + `File size is ${fileInfo.size} bytes. It exceeds the maximum limit of ${maxFileSize} bytes.`, ); } diff --git a/api/server/utils/import/importers-timestamp.spec.js b/api/server/utils/import/importers-timestamp.spec.js index c7665dfe25..3fbf5bf404 100644 --- a/api/server/utils/import/importers-timestamp.spec.js +++ b/api/server/utils/import/importers-timestamp.spec.js @@ -3,10 +3,8 @@ const { ImportBatchBuilder } = require('./importBatchBuilder'); const { getImporter } = require('./importers'); // Mock the database methods -jest.mock('~/models/Conversation', () => ({ +jest.mock('~/models', () => ({ bulkSaveConvos: jest.fn(), -})); -jest.mock('~/models/Message', () => ({ bulkSaveMessages: jest.fn(), })); jest.mock('~/cache/getLogStores'); diff --git a/api/server/utils/import/importers.spec.js b/api/server/utils/import/importers.spec.js index a695a31555..7984144cbc 100644 --- a/api/server/utils/import/importers.spec.js +++ b/api/server/utils/import/importers.spec.js @@ -1,10 +1,9 @@ const fs = require('fs'); const path = require('path'); const { EModelEndpoint, Constants, openAISettings } = require('librechat-data-provider'); -const { bulkSaveConvos: _bulkSaveConvos } = require('~/models/Conversation'); const { getImporter, processAssistantMessage } = require('./importers'); const { ImportBatchBuilder } = require('./importBatchBuilder'); -const { bulkSaveMessages } = require('~/models/Message'); +const { bulkSaveMessages, bulkSaveConvos: _bulkSaveConvos } = require('~/models'); const getLogStores = require('~/cache/getLogStores'); jest.mock('~/cache/getLogStores'); @@ -14,10 +13,8 @@ getLogStores.mockImplementation(() => ({ })); // Mock the database methods -jest.mock('~/models/Conversation', () => ({ +jest.mock('~/models', () => ({ bulkSaveConvos: jest.fn(), -})); -jest.mock('~/models/Message', () => ({ bulkSaveMessages: jest.fn(), })); @@ -1277,12 +1274,9 @@ describe('processAssistantMessage', () => { results.push(duration); }); - // Check if processing time increases exponentially - // In a ReDoS vulnerability, time would roughly double with each size increase - for (let i = 1; i < results.length; i++) { - const ratio = results[i] / results[i - 1]; - expect(ratio).toBeLessThan(3); // Allow for CI environment variability while still catching ReDoS - console.log(`Size ${sizes[i]} processing time ratio: ${ratio}`); + // Each size should complete well under 100ms; a ReDoS would cause exponential blowup + for (let i = 0; i < results.length; i++) { + expect(results[i]).toBeLessThan(100); } // Also test with the exact payload from the security report diff --git a/api/strategies/index.js b/api/strategies/index.js index b4f7bd3cac..9a1c58ad38 100644 --- a/api/strategies/index.js +++ b/api/strategies/index.js @@ -1,4 +1,4 @@ -const { setupOpenId, getOpenIdConfig } = require('./openidStrategy'); +const { setupOpenId, getOpenIdConfig, getOpenIdEmail } = require('./openidStrategy'); const openIdJwtLogin = require('./openIdJwtStrategy'); const facebookLogin = require('./facebookStrategy'); const discordLogin = require('./discordStrategy'); @@ -20,6 +20,7 @@ module.exports = { facebookLogin, setupOpenId, getOpenIdConfig, + getOpenIdEmail, ldapLogin, setupSaml, openIdJwtLogin, diff --git a/api/strategies/localStrategy.js b/api/strategies/localStrategy.js index 0d220ead25..5d725c0907 100644 --- a/api/strategies/localStrategy.js +++ b/api/strategies/localStrategy.js @@ -1,8 +1,9 @@ +const bcrypt = require('bcryptjs'); const { logger } = require('@librechat/data-schemas'); const { errorsToString } = require('librechat-data-provider'); -const { isEnabled, checkEmailConfig } = require('@librechat/api'); const { Strategy: PassportLocalStrategy } = require('passport-local'); -const { findUser, comparePassword, updateUser } = require('~/models'); +const { isEnabled, checkEmailConfig, comparePassword } = require('@librechat/api'); +const { findUser, updateUser } = require('~/models'); const { loginSchema } = require('./validators'); // Unix timestamp for 2024-06-07 15:20:18 Eastern Time @@ -35,7 +36,7 @@ async function passportLogin(req, email, password, done) { return done(null, false, { message: 'Email does not exist.' }); } - const isMatch = await comparePassword(user, password); + const isMatch = await comparePassword(user, password, { compare: bcrypt.compare }); if (!isMatch) { logError('Passport Local Strategy - Password does not match', { isMatch }); logger.error(`[Login] [Login failed] [Username: ${email}] [Request-IP: ${req.ip}]`); diff --git a/api/strategies/openIdJwtStrategy.js b/api/strategies/openIdJwtStrategy.js index 997dcec397..83a40bf948 100644 --- a/api/strategies/openIdJwtStrategy.js +++ b/api/strategies/openIdJwtStrategy.js @@ -5,6 +5,7 @@ const { HttpsProxyAgent } = require('https-proxy-agent'); const { SystemRoles } = require('librechat-data-provider'); const { isEnabled, findOpenIDUser, math } = require('@librechat/api'); const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt'); +const { getOpenIdEmail } = require('./openidStrategy'); const { updateUser, findUser } = require('~/models'); /** @@ -53,7 +54,7 @@ const openIdJwtLogin = (openIdConfig) => { const { user, error, migration } = await findOpenIDUser({ findUser, - email: payload?.email, + email: payload ? getOpenIdEmail(payload) : undefined, openidId: payload?.sub, idOnTheSource: payload?.oid, strategyName: 'openIdJwtLogin', diff --git a/api/strategies/openIdJwtStrategy.spec.js b/api/strategies/openIdJwtStrategy.spec.js index 566afe5a90..79af848046 100644 --- a/api/strategies/openIdJwtStrategy.spec.js +++ b/api/strategies/openIdJwtStrategy.spec.js @@ -29,10 +29,21 @@ jest.mock('~/models', () => ({ findUser: jest.fn(), updateUser: jest.fn(), })); +jest.mock('~/server/services/Files/strategies', () => ({ + getStrategyFunctions: jest.fn(() => ({ + saveBuffer: jest.fn().mockResolvedValue('/fake/path/to/avatar.png'), + })), +})); +jest.mock('~/server/services/Config', () => ({ + getAppConfig: jest.fn().mockResolvedValue({}), +})); +jest.mock('~/cache/getLogStores', () => + jest.fn().mockReturnValue({ get: jest.fn(), set: jest.fn() }), +); const { findOpenIDUser } = require('@librechat/api'); -const { updateUser } = require('~/models'); const openIdJwtLogin = require('./openIdJwtStrategy'); +const { findUser, updateUser } = require('~/models'); // Helper: build a mock openIdConfig const mockOpenIdConfig = { @@ -181,3 +192,156 @@ describe('openIdJwtStrategy – token source handling', () => { expect(user.federatedTokens.access_token).not.toBe(user.federatedTokens.id_token); }); }); + +describe('openIdJwtStrategy – OPENID_EMAIL_CLAIM', () => { + const payload = { + sub: 'oidc-123', + email: 'test@example.com', + preferred_username: 'testuser', + upn: 'test@corp.example.com', + exp: 9999999999, + }; + + beforeEach(() => { + jest.clearAllMocks(); + delete process.env.OPENID_EMAIL_CLAIM; + + // Use real findOpenIDUser so it delegates to the findUser mock + const realFindOpenIDUser = jest.requireActual('@librechat/api').findOpenIDUser; + findOpenIDUser.mockImplementation(realFindOpenIDUser); + + findUser.mockResolvedValue(null); + updateUser.mockResolvedValue({}); + + openIdJwtLogin(mockOpenIdConfig); + }); + + afterEach(() => { + delete process.env.OPENID_EMAIL_CLAIM; + }); + + it('should use the default email when OPENID_EMAIL_CLAIM is not set', async () => { + const existingUser = { + _id: 'user-id-1', + provider: 'openid', + openidId: payload.sub, + email: payload.email, + role: SystemRoles.USER, + }; + findUser.mockImplementation(async (query) => { + if (query.$or && query.$or.some((c) => c.openidId === payload.sub)) { + return existingUser; + } + return null; + }); + + const req = { headers: { authorization: 'Bearer tok' }, session: {} }; + await invokeVerify(req, payload); + + expect(findUser).toHaveBeenCalledWith( + expect.objectContaining({ + $or: expect.arrayContaining([{ openidId: payload.sub }]), + }), + ); + }); + + it('should use OPENID_EMAIL_CLAIM when set for email lookup', async () => { + process.env.OPENID_EMAIL_CLAIM = 'upn'; + findUser.mockResolvedValue(null); + + const req = { headers: { authorization: 'Bearer tok' }, session: {} }; + const { user } = await invokeVerify(req, payload); + + expect(findUser).toHaveBeenCalledTimes(2); + expect(findUser.mock.calls[0][0]).toMatchObject({ + $or: expect.arrayContaining([{ openidId: payload.sub }]), + }); + expect(findUser.mock.calls[1][0]).toEqual({ email: 'test@corp.example.com' }); + expect(user).toBe(false); + }); + + it('should fall back to default chain when OPENID_EMAIL_CLAIM points to missing claim', async () => { + process.env.OPENID_EMAIL_CLAIM = 'nonexistent_claim'; + findUser.mockResolvedValue(null); + + const req = { headers: { authorization: 'Bearer tok' }, session: {} }; + const { user } = await invokeVerify(req, payload); + + expect(findUser).toHaveBeenCalledWith({ email: payload.email }); + expect(user).toBe(false); + }); + + it('should trim whitespace from OPENID_EMAIL_CLAIM', async () => { + process.env.OPENID_EMAIL_CLAIM = ' upn '; + findUser.mockResolvedValue(null); + + const req = { headers: { authorization: 'Bearer tok' }, session: {} }; + await invokeVerify(req, payload); + + expect(findUser).toHaveBeenCalledWith({ email: 'test@corp.example.com' }); + }); + + it('should ignore empty string OPENID_EMAIL_CLAIM and use default fallback', async () => { + process.env.OPENID_EMAIL_CLAIM = ''; + findUser.mockResolvedValue(null); + + const req = { headers: { authorization: 'Bearer tok' }, session: {} }; + await invokeVerify(req, payload); + + expect(findUser).toHaveBeenCalledWith({ email: payload.email }); + }); + + it('should ignore whitespace-only OPENID_EMAIL_CLAIM and use default fallback', async () => { + process.env.OPENID_EMAIL_CLAIM = ' '; + findUser.mockResolvedValue(null); + + const req = { headers: { authorization: 'Bearer tok' }, session: {} }; + await invokeVerify(req, payload); + + expect(findUser).toHaveBeenCalledWith({ email: payload.email }); + }); + + it('should resolve undefined email when payload is null', async () => { + const req = { headers: { authorization: 'Bearer tok' }, session: {} }; + const { user } = await invokeVerify(req, null); + + expect(user).toBe(false); + }); + + it('should attempt email lookup via preferred_username fallback when email claim is absent', async () => { + const payloadNoEmail = { + sub: 'oidc-new-sub', + preferred_username: 'legacy@corp.com', + upn: 'legacy@corp.com', + exp: 9999999999, + }; + + const legacyUser = { + _id: 'legacy-db-id', + email: 'legacy@corp.com', + openidId: null, + role: SystemRoles.USER, + }; + + findUser.mockImplementation(async (query) => { + if (query.$or) { + return null; + } + if (query.email === 'legacy@corp.com') { + return legacyUser; + } + return null; + }); + + const req = { headers: { authorization: 'Bearer tok' }, session: {} }; + const { user } = await invokeVerify(req, payloadNoEmail); + + expect(findUser).toHaveBeenCalledTimes(2); + expect(findUser.mock.calls[1][0]).toEqual({ email: 'legacy@corp.com' }); + expect(user).toBeTruthy(); + expect(updateUser).toHaveBeenCalledWith( + 'legacy-db-id', + expect.objectContaining({ provider: 'openid', openidId: payloadNoEmail.sub }), + ); + }); +}); diff --git a/api/strategies/openidStrategy.js b/api/strategies/openidStrategy.js index 198c8735ae..7c43358297 100644 --- a/api/strategies/openidStrategy.js +++ b/api/strategies/openidStrategy.js @@ -267,6 +267,34 @@ function getFullName(userinfo) { return userinfo.username || userinfo.email; } +/** + * Resolves the user identifier from OpenID claims. + * Configurable via OPENID_EMAIL_CLAIM; defaults to: email -> preferred_username -> upn. + * + * @param {Object} userinfo - The user information object from OpenID Connect + * @returns {string|undefined} The resolved identifier string + */ +function getOpenIdEmail(userinfo) { + const claimKey = process.env.OPENID_EMAIL_CLAIM?.trim(); + if (claimKey) { + const value = userinfo[claimKey]; + if (typeof value === 'string' && value) { + return value; + } + if (value !== undefined && value !== null) { + logger.warn( + `[openidStrategy] OPENID_EMAIL_CLAIM="${claimKey}" resolved to a non-string value (type: ${typeof value}). Falling back to: email -> preferred_username -> upn.`, + ); + } else { + logger.warn( + `[openidStrategy] OPENID_EMAIL_CLAIM="${claimKey}" not present in userinfo. Falling back to: email -> preferred_username -> upn.`, + ); + } + } + const fallback = userinfo.email || userinfo.preferred_username || userinfo.upn; + return typeof fallback === 'string' ? fallback : undefined; +} + /** * Converts an input into a string suitable for a username. * If the input is a string, it will be returned as is. @@ -287,24 +315,85 @@ function convertToUsername(input, defaultValue = '') { return defaultValue; } +/** + * Exchange the access token for a Graph-scoped token using the On-Behalf-Of (OBO) flow. + * + * The original access token has the app's own audience (api://), which Microsoft Graph + * rejects. This exchange produces a token with audience https://graph.microsoft.com and the + * minimum delegated scope (User.Read) required by /me/getMemberObjects. + * + * Uses a dedicated cache key (`${sub}:overage`) to avoid collisions with other OBO exchanges + * in the codebase (userinfo, Graph principal search). + * + * @param {string} accessToken - The original access token from the OpenID tokenset + * @param {string} sub - The subject identifier for cache keying + * @returns {Promise} A Graph-scoped access token + * @see https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-on-behalf-of-flow + */ +async function exchangeTokenForOverage(accessToken, sub) { + if (!openidConfig) { + throw new Error('[openidStrategy] OpenID config not initialized; cannot exchange OBO token'); + } + + const tokensCache = getLogStores(CacheKeys.OPENID_EXCHANGED_TOKENS); + const cacheKey = `${sub}:overage`; + + const cached = await tokensCache.get(cacheKey); + if (cached?.access_token) { + logger.debug('[openidStrategy] Using cached Graph token for overage resolution'); + return cached.access_token; + } + + const grantResponse = await client.genericGrantRequest( + openidConfig, + 'urn:ietf:params:oauth:grant-type:jwt-bearer', + { + scope: 'https://graph.microsoft.com/User.Read', + assertion: accessToken, + requested_token_use: 'on_behalf_of', + }, + ); + + if (!grantResponse.access_token) { + throw new Error( + '[openidStrategy] OBO exchange succeeded but returned no access_token; cannot call Graph API', + ); + } + + const ttlMs = + Number.isFinite(grantResponse.expires_in) && grantResponse.expires_in > 0 + ? grantResponse.expires_in * 1000 + : 3600 * 1000; + + await tokensCache.set(cacheKey, { access_token: grantResponse.access_token }, ttlMs); + + return grantResponse.access_token; +} + /** * Resolve Azure AD groups when group overage is in effect (groups moved to _claim_names/_claim_sources). * * NOTE: Microsoft recommends treating _claim_names/_claim_sources as a signal only and using Microsoft Graph * to resolve group membership instead of calling the endpoint in _claim_sources directly. * - * @param {string} accessToken - Access token with Microsoft Graph permissions + * Before calling Graph, the access token is exchanged via the OBO flow to obtain a token with the + * correct audience (https://graph.microsoft.com) and User.Read scope. + * + * @param {string} accessToken - Access token from the OpenID tokenset (app audience) + * @param {string} sub - The subject identifier of the user (for OBO exchange and cache keying) * @returns {Promise} Resolved group IDs or null on failure * @see https://learn.microsoft.com/en-us/entra/identity-platform/access-token-claims-reference#groups-overage-claim * @see https://learn.microsoft.com/en-us/graph/api/directoryobject-getmemberobjects */ -async function resolveGroupsFromOverage(accessToken) { +async function resolveGroupsFromOverage(accessToken, sub) { try { if (!accessToken) { logger.error('[openidStrategy] Access token missing; cannot resolve group overage'); return null; } + const graphToken = await exchangeTokenForOverage(accessToken, sub); + // Use /me/getMemberObjects so least-privileged delegated permission User.Read is sufficient // when resolving the signed-in user's group membership. const url = 'https://graph.microsoft.com/v1.0/me/getMemberObjects'; @@ -316,7 +405,7 @@ async function resolveGroupsFromOverage(accessToken) { const fetchOptions = { method: 'POST', headers: { - Authorization: `Bearer ${accessToken}`, + Authorization: `Bearer ${graphToken}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ securityEnabledOnly: false }), @@ -336,6 +425,7 @@ async function resolveGroupsFromOverage(accessToken) { } const data = await response.json(); + const values = Array.isArray(data?.value) ? data.value : null; if (!values) { logger.error( @@ -379,11 +469,10 @@ async function processOpenIDAuth(tokenset, existingUsersOnly = false) { } const appConfig = await getAppConfig(); - /** Azure AD sometimes doesn't return email, use preferred_username as fallback */ - const email = userinfo.email || userinfo.preferred_username || userinfo.upn; + const email = getOpenIdEmail(userinfo); if (!isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) { logger.error( - `[OpenID Strategy] Authentication blocked - email domain not allowed [Email: ${userinfo.email}]`, + `[OpenID Strategy] Authentication blocked - email domain not allowed [Identifier: ${email}]`, ); throw new Error('Email domain not allowed'); } @@ -405,6 +494,8 @@ async function processOpenIDAuth(tokenset, existingUsersOnly = false) { const fullName = getFullName(userinfo); const requiredRole = process.env.OPENID_REQUIRED_ROLE; + let resolvedOverageGroups = null; + if (requiredRole) { const requiredRoles = requiredRole .split(',') @@ -424,19 +515,21 @@ async function processOpenIDAuth(tokenset, existingUsersOnly = false) { // Handle Azure AD group overage for ID token groups: when hasgroups or _claim_* indicate overage, // resolve groups via Microsoft Graph instead of relying on token group values. + const hasOverage = + decodedToken?.hasgroups || + (decodedToken?._claim_names?.groups && + decodedToken?._claim_sources?.[decodedToken._claim_names.groups]); + if ( - !Array.isArray(roles) && - typeof roles !== 'string' && requiredRoleTokenKind === 'id' && requiredRoleParameterPath === 'groups' && decodedToken && - (decodedToken.hasgroups || - (decodedToken._claim_names?.groups && - decodedToken._claim_sources?.[decodedToken._claim_names.groups])) + hasOverage ) { - const overageGroups = await resolveGroupsFromOverage(tokenset.access_token); + const overageGroups = await resolveGroupsFromOverage(tokenset.access_token, claims.sub); if (overageGroups) { roles = overageGroups; + resolvedOverageGroups = overageGroups; } } @@ -451,7 +544,7 @@ async function processOpenIDAuth(tokenset, existingUsersOnly = false) { throw new Error(`You must have ${rolesList} role to log in.`); } - const roleValues = Array.isArray(roles) ? roles : [roles]; + const roleValues = Array.isArray(roles) ? roles : roles.split(/[\s,]+/).filter(Boolean); if (!requiredRoles.some((role) => roleValues.includes(role))) { const rolesList = @@ -523,14 +616,33 @@ async function processOpenIDAuth(tokenset, existingUsersOnly = false) { throw new Error('Invalid admin role token kind'); } - const adminRoles = get(adminRoleObject, adminRoleParameterPath); + let adminRoles = get(adminRoleObject, adminRoleParameterPath); - if ( - adminRoles && - (adminRoles === true || - adminRoles === adminRole || - (Array.isArray(adminRoles) && adminRoles.includes(adminRole))) - ) { + // Handle Azure AD group overage for admin role when using ID token groups + if (adminRoleTokenKind === 'id' && adminRoleParameterPath === 'groups' && adminRoleObject) { + const hasAdminOverage = + adminRoleObject.hasgroups || + (adminRoleObject._claim_names?.groups && + adminRoleObject._claim_sources?.[adminRoleObject._claim_names.groups]); + + if (hasAdminOverage) { + const overageGroups = + resolvedOverageGroups || + (await resolveGroupsFromOverage(tokenset.access_token, claims.sub)); + if (overageGroups) { + adminRoles = overageGroups; + } + } + } + + let adminRoleValues = []; + if (Array.isArray(adminRoles)) { + adminRoleValues = adminRoles; + } else if (typeof adminRoles === 'string') { + adminRoleValues = adminRoles.split(/[\s,]+/).filter(Boolean); + } + + if (adminRoles && (adminRoles === true || adminRoleValues.includes(adminRole))) { user.role = SystemRoles.ADMIN; logger.info(`[openidStrategy] User ${username} is an admin based on role: ${adminRole}`); } else if (user.role === SystemRoles.ADMIN) { @@ -727,4 +839,5 @@ function getOpenIdConfig() { module.exports = { setupOpenId, getOpenIdConfig, + getOpenIdEmail, }; diff --git a/api/strategies/openidStrategy.spec.js b/api/strategies/openidStrategy.spec.js index b1dc54d77b..16fa548a59 100644 --- a/api/strategies/openidStrategy.spec.js +++ b/api/strategies/openidStrategy.spec.js @@ -1,6 +1,6 @@ +const undici = require('undici'); const fetch = require('node-fetch'); const jwtDecode = require('jsonwebtoken/decode'); -const undici = require('undici'); const { ErrorTypes } = require('librechat-data-provider'); const { findUser, createUser, updateUser } = require('~/models'); const { setupOpenId } = require('./openidStrategy'); @@ -64,6 +64,10 @@ jest.mock('openid-client', () => { // Only return additional properties, but don't override any claims return Promise.resolve({}); }), + genericGrantRequest: jest.fn().mockResolvedValue({ + access_token: 'exchanged_graph_token', + expires_in: 3600, + }), customFetch: Symbol('customFetch'), }; }); @@ -152,6 +156,7 @@ describe('setupOpenId', () => { process.env.OPENID_ADMIN_ROLE_TOKEN_KIND = 'id'; delete process.env.OPENID_USERNAME_CLAIM; delete process.env.OPENID_NAME_CLAIM; + delete process.env.OPENID_EMAIL_CLAIM; delete process.env.PROXY; delete process.env.OPENID_USE_PKCE; @@ -384,6 +389,62 @@ describe('setupOpenId', () => { expect(details.message).toBe('You must have "read" role to log in.'); }); + it('should allow login when roles claim is a space-separated string containing the required role', async () => { + // Arrange – IdP returns roles as a space-delimited string + jwtDecode.mockReturnValue({ + roles: 'role1 role2 requiredRole', + }); + + // Act + const { user } = await validate(tokenset); + + // Assert – login succeeds when required role is present after splitting + expect(user).toBeTruthy(); + expect(createUser).toHaveBeenCalled(); + }); + + it('should allow login when roles claim is a comma-separated string containing the required role', async () => { + // Arrange – IdP returns roles as a comma-delimited string + jwtDecode.mockReturnValue({ + roles: 'role1,role2,requiredRole', + }); + + // Act + const { user } = await validate(tokenset); + + // Assert – login succeeds when required role is present after splitting + expect(user).toBeTruthy(); + expect(createUser).toHaveBeenCalled(); + }); + + it('should allow login when roles claim is a mixed comma-and-space-separated string containing the required role', async () => { + // Arrange – IdP returns roles with comma-and-space delimiters + jwtDecode.mockReturnValue({ + roles: 'role1, role2, requiredRole', + }); + + // Act + const { user } = await validate(tokenset); + + // Assert – login succeeds when required role is present after splitting + expect(user).toBeTruthy(); + expect(createUser).toHaveBeenCalled(); + }); + + it('should reject login when roles claim is a space-separated string that does not contain the required role', async () => { + // Arrange – IdP returns a delimited string but required role is absent + jwtDecode.mockReturnValue({ + roles: 'role1 role2 otherRole', + }); + + // Act + const { user, details } = await validate(tokenset); + + // Assert – login is rejected with the correct error message + expect(user).toBe(false); + expect(details.message).toBe('You must have "requiredRole" role to log in.'); + }); + it('should allow login when single required role is present (backward compatibility)', async () => { // Arrange – ensure single role configuration (as set in beforeEach) // OPENID_REQUIRED_ROLE = 'requiredRole' @@ -673,7 +734,7 @@ describe('setupOpenId', () => { expect.objectContaining({ method: 'POST', headers: expect.objectContaining({ - Authorization: `Bearer ${tokenset.access_token}`, + Authorization: 'Bearer exchanged_graph_token', }), }), ); @@ -688,6 +749,313 @@ describe('setupOpenId', () => { ); }); + describe('OBO token exchange for overage', () => { + it('exchanges access token via OBO before calling Graph API', async () => { + const openidClient = require('openid-client'); + process.env.OPENID_REQUIRED_ROLE = 'group-required'; + process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id'; + + jwtDecode.mockReturnValue({ hasgroups: true }); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + undici.fetch.mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + json: async () => ({ value: ['group-required'] }), + }); + + await validate(tokenset); + + expect(openidClient.genericGrantRequest).toHaveBeenCalledWith( + expect.anything(), + 'urn:ietf:params:oauth:grant-type:jwt-bearer', + expect.objectContaining({ + scope: 'https://graph.microsoft.com/User.Read', + assertion: tokenset.access_token, + requested_token_use: 'on_behalf_of', + }), + ); + + expect(undici.fetch).toHaveBeenCalledWith( + 'https://graph.microsoft.com/v1.0/me/getMemberObjects', + expect.objectContaining({ + headers: expect.objectContaining({ + Authorization: 'Bearer exchanged_graph_token', + }), + }), + ); + }); + + it('caches the exchanged token and reuses it on subsequent calls', async () => { + const openidClient = require('openid-client'); + const getLogStores = require('~/cache/getLogStores'); + const mockSet = jest.fn(); + const mockGet = jest + .fn() + .mockResolvedValueOnce(undefined) + .mockResolvedValueOnce({ access_token: 'exchanged_graph_token' }); + getLogStores.mockReturnValue({ get: mockGet, set: mockSet }); + + process.env.OPENID_REQUIRED_ROLE = 'group-required'; + process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id'; + + jwtDecode.mockReturnValue({ hasgroups: true }); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + undici.fetch.mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + json: async () => ({ value: ['group-required'] }), + }); + + // First call: cache miss → OBO exchange → cache set + await validate(tokenset); + expect(mockSet).toHaveBeenCalledWith( + '1234:overage', + { access_token: 'exchanged_graph_token' }, + 3600000, + ); + expect(openidClient.genericGrantRequest).toHaveBeenCalledTimes(1); + + // Second call: cache hit → no new OBO exchange + openidClient.genericGrantRequest.mockClear(); + await validate(tokenset); + expect(openidClient.genericGrantRequest).not.toHaveBeenCalled(); + }); + }); + + describe('admin role group overage', () => { + it('resolves admin groups via Graph when overage is detected for admin role', async () => { + process.env.OPENID_REQUIRED_ROLE = 'group-required'; + process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id'; + process.env.OPENID_ADMIN_ROLE = 'admin-group-id'; + process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_ADMIN_ROLE_TOKEN_KIND = 'id'; + + jwtDecode.mockReturnValue({ hasgroups: true }); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + undici.fetch.mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + json: async () => ({ value: ['group-required', 'admin-group-id'] }), + }); + + const { user } = await validate(tokenset); + + expect(user.role).toBe('ADMIN'); + }); + + it('does not grant admin when overage groups do not contain admin role', async () => { + process.env.OPENID_REQUIRED_ROLE = 'group-required'; + process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id'; + process.env.OPENID_ADMIN_ROLE = 'admin-group-id'; + process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_ADMIN_ROLE_TOKEN_KIND = 'id'; + + jwtDecode.mockReturnValue({ hasgroups: true }); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + undici.fetch.mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + json: async () => ({ value: ['group-required', 'other-group'] }), + }); + + const { user } = await validate(tokenset); + + expect(user).toBeTruthy(); + expect(user.role).toBeUndefined(); + }); + + it('reuses already-resolved overage groups for admin role check (no duplicate Graph call)', async () => { + process.env.OPENID_REQUIRED_ROLE = 'group-required'; + process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id'; + process.env.OPENID_ADMIN_ROLE = 'admin-group-id'; + process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_ADMIN_ROLE_TOKEN_KIND = 'id'; + + jwtDecode.mockReturnValue({ hasgroups: true }); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + undici.fetch.mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + json: async () => ({ value: ['group-required', 'admin-group-id'] }), + }); + + await validate(tokenset); + + // Graph API should be called only once (for required role), admin role reuses the result + expect(undici.fetch).toHaveBeenCalledTimes(1); + }); + + it('demotes existing admin when overage groups no longer contain admin role', async () => { + process.env.OPENID_REQUIRED_ROLE = 'group-required'; + process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id'; + process.env.OPENID_ADMIN_ROLE = 'admin-group-id'; + process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_ADMIN_ROLE_TOKEN_KIND = 'id'; + + const existingAdminUser = { + _id: 'existingAdminId', + provider: 'openid', + email: tokenset.claims().email, + openidId: tokenset.claims().sub, + username: 'adminuser', + name: 'Admin User', + role: 'ADMIN', + }; + + findUser.mockImplementation(async (query) => { + if (query.openidId === tokenset.claims().sub || query.email === tokenset.claims().email) { + return existingAdminUser; + } + return null; + }); + + jwtDecode.mockReturnValue({ hasgroups: true }); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + undici.fetch.mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + json: async () => ({ value: ['group-required'] }), + }); + + const { user } = await validate(tokenset); + + expect(user.role).toBe('USER'); + }); + + it('does not attempt overage for admin role when token kind is not id', async () => { + process.env.OPENID_REQUIRED_ROLE = 'requiredRole'; + process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'roles'; + process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id'; + process.env.OPENID_ADMIN_ROLE = 'admin'; + process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_ADMIN_ROLE_TOKEN_KIND = 'access'; + + jwtDecode.mockReturnValue({ + roles: ['requiredRole'], + hasgroups: true, + }); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + const { user } = await validate(tokenset); + + // No Graph call since admin uses access token (not id) + expect(undici.fetch).not.toHaveBeenCalled(); + expect(user.role).toBeUndefined(); + }); + + it('resolves admin via Graph independently when OPENID_REQUIRED_ROLE is not configured', async () => { + delete process.env.OPENID_REQUIRED_ROLE; + process.env.OPENID_ADMIN_ROLE = 'admin-group-id'; + process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_ADMIN_ROLE_TOKEN_KIND = 'id'; + + jwtDecode.mockReturnValue({ hasgroups: true }); + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + undici.fetch.mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + json: async () => ({ value: ['admin-group-id'] }), + }); + + const { user } = await validate(tokenset); + expect(user.role).toBe('ADMIN'); + expect(undici.fetch).toHaveBeenCalledTimes(1); + }); + + it('denies admin when OPENID_REQUIRED_ROLE is absent and Graph does not contain admin group', async () => { + delete process.env.OPENID_REQUIRED_ROLE; + process.env.OPENID_ADMIN_ROLE = 'admin-group-id'; + process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_ADMIN_ROLE_TOKEN_KIND = 'id'; + + jwtDecode.mockReturnValue({ hasgroups: true }); + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + undici.fetch.mockResolvedValue({ + ok: true, + status: 200, + statusText: 'OK', + json: async () => ({ value: ['other-group'] }), + }); + + const { user } = await validate(tokenset); + expect(user).toBeTruthy(); + expect(user.role).toBeUndefined(); + }); + + it('denies login and logs error when OBO exchange throws', async () => { + const openidClient = require('openid-client'); + process.env.OPENID_REQUIRED_ROLE = 'group-required'; + process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id'; + + jwtDecode.mockReturnValue({ hasgroups: true }); + openidClient.genericGrantRequest.mockRejectedValueOnce(new Error('OBO exchange rejected')); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + const { user, details } = await validate(tokenset); + expect(user).toBe(false); + expect(details.message).toBe('You must have "group-required" role to log in.'); + expect(undici.fetch).not.toHaveBeenCalled(); + }); + + it('denies login when OBO exchange returns no access_token', async () => { + const openidClient = require('openid-client'); + process.env.OPENID_REQUIRED_ROLE = 'group-required'; + process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'groups'; + process.env.OPENID_REQUIRED_ROLE_TOKEN_KIND = 'id'; + + jwtDecode.mockReturnValue({ hasgroups: true }); + openidClient.genericGrantRequest.mockResolvedValueOnce({ expires_in: 3600 }); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + const { user, details } = await validate(tokenset); + expect(user).toBe(false); + expect(details.message).toBe('You must have "group-required" role to log in.'); + expect(undici.fetch).not.toHaveBeenCalled(); + }); + }); + it('should attempt to download and save the avatar if picture is provided', async () => { // Act const { user } = await validate(tokenset); @@ -1182,6 +1550,46 @@ describe('setupOpenId', () => { expect(user.role).toBeUndefined(); }); + it('should grant admin when admin role claim is a space-separated string containing the admin role', async () => { + // Arrange – IdP returns admin roles as a space-delimited string + process.env.OPENID_ADMIN_ROLE = 'site-admin'; + process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'app_roles'; + + jwtDecode.mockReturnValue({ + roles: ['requiredRole'], + app_roles: 'user site-admin moderator', + }); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + // Act + const { user } = await validate(tokenset); + + // Assert – admin role is granted after splitting the delimited string + expect(user.role).toBe('ADMIN'); + }); + + it('should not grant admin when admin role claim is a space-separated string that does not contain the admin role', async () => { + // Arrange – delimited string present but admin role is absent + process.env.OPENID_ADMIN_ROLE = 'site-admin'; + process.env.OPENID_ADMIN_ROLE_PARAMETER_PATH = 'app_roles'; + + jwtDecode.mockReturnValue({ + roles: ['requiredRole'], + app_roles: 'user moderator', + }); + + await setupOpenId(); + verifyCallback = require('openid-client/passport').__getVerifyCallbackByName('openid'); + + // Act + const { user } = await validate(tokenset); + + // Assert – admin role is not granted + expect(user.role).toBeUndefined(); + }); + it('should handle nested path with special characters in keys', async () => { process.env.OPENID_REQUIRED_ROLE = 'app-user'; process.env.OPENID_REQUIRED_ROLE_PARAMETER_PATH = 'resource_access.my-app-123.roles'; @@ -1306,4 +1714,82 @@ describe('setupOpenId', () => { expect(user).toBe(false); }); }); + + describe('OPENID_EMAIL_CLAIM', () => { + it('should use the default email when OPENID_EMAIL_CLAIM is not set', async () => { + const { user } = await validate(tokenset); + expect(user.email).toBe('test@example.com'); + }); + + it('should use the configured claim when OPENID_EMAIL_CLAIM is set', async () => { + process.env.OPENID_EMAIL_CLAIM = 'upn'; + const userinfo = { ...tokenset.claims(), upn: 'user@corp.example.com' }; + + const { user } = await validate({ ...tokenset, claims: () => userinfo }); + + expect(user.email).toBe('user@corp.example.com'); + expect(createUser).toHaveBeenCalledWith( + expect.objectContaining({ email: 'user@corp.example.com' }), + expect.anything(), + true, + true, + ); + }); + + it('should fall back to preferred_username when email is missing and OPENID_EMAIL_CLAIM is not set', async () => { + const userinfo = { ...tokenset.claims() }; + delete userinfo.email; + + const { user } = await validate({ ...tokenset, claims: () => userinfo }); + + expect(user.email).toBe('testusername'); + }); + + it('should fall back to upn when email and preferred_username are missing and OPENID_EMAIL_CLAIM is not set', async () => { + const userinfo = { ...tokenset.claims(), upn: 'user@corp.example.com' }; + delete userinfo.email; + delete userinfo.preferred_username; + + const { user } = await validate({ ...tokenset, claims: () => userinfo }); + + expect(user.email).toBe('user@corp.example.com'); + }); + + it('should ignore empty string OPENID_EMAIL_CLAIM and use default fallback', async () => { + process.env.OPENID_EMAIL_CLAIM = ''; + + const { user } = await validate(tokenset); + + expect(user.email).toBe('test@example.com'); + }); + + it('should trim whitespace from OPENID_EMAIL_CLAIM and resolve correctly', async () => { + process.env.OPENID_EMAIL_CLAIM = ' upn '; + const userinfo = { ...tokenset.claims(), upn: 'user@corp.example.com' }; + + const { user } = await validate({ ...tokenset, claims: () => userinfo }); + + expect(user.email).toBe('user@corp.example.com'); + }); + + it('should ignore whitespace-only OPENID_EMAIL_CLAIM and use default fallback', async () => { + process.env.OPENID_EMAIL_CLAIM = ' '; + + const { user } = await validate(tokenset); + + expect(user.email).toBe('test@example.com'); + }); + + it('should fall back to default chain with warning when configured claim is missing from userinfo', async () => { + const { logger } = require('@librechat/data-schemas'); + process.env.OPENID_EMAIL_CLAIM = 'nonexistent_claim'; + + const { user } = await validate(tokenset); + + expect(user.email).toBe('test@example.com'); + expect(logger.warn).toHaveBeenCalledWith( + expect.stringContaining('OPENID_EMAIL_CLAIM="nonexistent_claim" not present in userinfo'), + ); + }); + }); }); diff --git a/api/strategies/samlStrategy.spec.js b/api/strategies/samlStrategy.spec.js index 06c969ce46..1d16719b87 100644 --- a/api/strategies/samlStrategy.spec.js +++ b/api/strategies/samlStrategy.spec.js @@ -1,5 +1,4 @@ // --- Mocks --- -jest.mock('tiktoken'); jest.mock('fs'); jest.mock('path'); jest.mock('node-fetch'); diff --git a/api/test/server/services/Files/S3/crud.test.js b/api/test/server/services/Files/S3/crud.test.js deleted file mode 100644 index d847a82cf0..0000000000 --- a/api/test/server/services/Files/S3/crud.test.js +++ /dev/null @@ -1,72 +0,0 @@ -const { getS3URL } = require('../../../../../server/services/Files/S3/crud'); - -// Mock AWS SDK -jest.mock('@aws-sdk/client-s3', () => ({ - S3Client: jest.fn(() => ({ - send: jest.fn(), - })), - GetObjectCommand: jest.fn(), -})); - -jest.mock('@aws-sdk/s3-request-presigner', () => ({ - getSignedUrl: jest.fn(), -})); - -jest.mock('../../../../../config', () => ({ - logger: { - error: jest.fn(), - }, -})); - -const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); -const { GetObjectCommand } = require('@aws-sdk/client-s3'); - -describe('S3 crud.js - test only new parameter changes', () => { - beforeEach(() => { - jest.clearAllMocks(); - process.env.AWS_BUCKET_NAME = 'test-bucket'; - }); - - // Test only the new customFilename parameter - it('should include customFilename in response headers when provided', async () => { - getSignedUrl.mockResolvedValue('https://test-presigned-url.com'); - - await getS3URL({ - userId: 'user123', - fileName: 'test.pdf', - customFilename: 'cleaned_filename.pdf', - }); - - // Verify the new ResponseContentDisposition parameter is added to GetObjectCommand - const commandArgs = GetObjectCommand.mock.calls[0][0]; - expect(commandArgs.ResponseContentDisposition).toBe( - 'attachment; filename="cleaned_filename.pdf"', - ); - }); - - // Test only the new contentType parameter - it('should include contentType in response headers when provided', async () => { - getSignedUrl.mockResolvedValue('https://test-presigned-url.com'); - - await getS3URL({ - userId: 'user123', - fileName: 'test.pdf', - contentType: 'application/pdf', - }); - - // Verify the new ResponseContentType parameter is added to GetObjectCommand - const commandArgs = GetObjectCommand.mock.calls[0][0]; - expect(commandArgs.ResponseContentType).toBe('application/pdf'); - }); - - it('should work without new parameters (backward compatibility)', async () => { - getSignedUrl.mockResolvedValue('https://test-presigned-url.com'); - - const result = await getS3URL({ - userId: 'user123', - fileName: 'test.pdf', - }); - - expect(result).toBe('https://test-presigned-url.com'); - }); -}); diff --git a/api/test/services/Files/processFileCitations.test.js b/api/test/services/Files/processFileCitations.test.js index e9fe850ebd..8dd588afe9 100644 --- a/api/test/services/Files/processFileCitations.test.js +++ b/api/test/services/Files/processFileCitations.test.js @@ -7,12 +7,7 @@ const { // Mock dependencies jest.mock('~/models', () => ({ - Files: { - find: jest.fn().mockResolvedValue([]), - }, -})); - -jest.mock('~/models/Role', () => ({ + getFiles: jest.fn().mockResolvedValue([]), getRoleByName: jest.fn(), })); @@ -179,7 +174,7 @@ describe('processFileCitations', () => { }); describe('enhanceSourcesWithMetadata', () => { - const { Files } = require('~/models'); + const { getFiles } = require('~/models'); const mockCustomConfig = { fileStrategy: 'local', }; @@ -204,7 +199,7 @@ describe('processFileCitations', () => { }, ]; - Files.find.mockResolvedValue([ + getFiles.mockResolvedValue([ { file_id: 'file_123', filename: 'example_from_db.pdf', @@ -219,7 +214,7 @@ describe('processFileCitations', () => { const result = await enhanceSourcesWithMetadata(sources, mockCustomConfig); - expect(Files.find).toHaveBeenCalledWith({ file_id: { $in: ['file_123', 'file_456'] } }); + expect(getFiles).toHaveBeenCalledWith({ file_id: { $in: ['file_123', 'file_456'] } }); expect(result).toHaveLength(2); expect(result[0]).toEqual({ @@ -258,7 +253,7 @@ describe('processFileCitations', () => { }, ]; - Files.find.mockResolvedValue([ + getFiles.mockResolvedValue([ { file_id: 'file_123', filename: 'example_from_db.pdf', @@ -292,7 +287,7 @@ describe('processFileCitations', () => { }, ]; - Files.find.mockResolvedValue([]); + getFiles.mockResolvedValue([]); const result = await enhanceSourcesWithMetadata(sources, mockCustomConfig); @@ -317,7 +312,7 @@ describe('processFileCitations', () => { }, ]; - Files.find.mockRejectedValue(new Error('Database error')); + getFiles.mockRejectedValue(new Error('Database error')); const result = await enhanceSourcesWithMetadata(sources, mockCustomConfig); @@ -339,14 +334,14 @@ describe('processFileCitations', () => { { fileId: 'file_456', fileName: 'doc2.pdf', relevance: 0.7, type: 'file' }, ]; - Files.find.mockResolvedValue([ + getFiles.mockResolvedValue([ { file_id: 'file_123', filename: 'document1.pdf', source: 's3' }, { file_id: 'file_456', filename: 'document2.pdf', source: 'local' }, ]); await enhanceSourcesWithMetadata(sources, mockCustomConfig); - expect(Files.find).toHaveBeenCalledWith({ file_id: { $in: ['file_123', 'file_456'] } }); + expect(getFiles).toHaveBeenCalledWith({ file_id: { $in: ['file_123', 'file_456'] } }); }); }); }); diff --git a/api/utils/tokens.spec.js b/api/utils/tokens.spec.js index b3ab87cd07..53ae43fb66 100644 --- a/api/utils/tokens.spec.js +++ b/api/utils/tokens.spec.js @@ -1,13 +1,13 @@ /** Note: No hard-coded values should be used in this file. */ +const { EModelEndpoint } = require('librechat-data-provider'); const { - EModelEndpoint, maxTokensMap, matchModelName, getModelMaxTokens, maxOutputTokensMap, findMatchingPattern, -} = require('librechat-data-provider'); -const { processModelData } = require('@librechat/api'); + processModelData, +} = require('@librechat/api'); describe('getModelMaxTokens', () => { test('should return correct tokens for exact match', () => { @@ -200,6 +200,39 @@ describe('getModelMaxTokens', () => { ); }); + test('should return correct tokens for gpt-5.3 matches', () => { + expect(getModelMaxTokens('gpt-5.3')).toBe(maxTokensMap[EModelEndpoint.openAI]['gpt-5.3']); + expect(getModelMaxTokens('gpt-5.3-codex')).toBe(maxTokensMap[EModelEndpoint.openAI]['gpt-5.3']); + expect(getModelMaxTokens('openai/gpt-5.3')).toBe( + maxTokensMap[EModelEndpoint.openAI]['gpt-5.3'], + ); + expect(getModelMaxTokens('gpt-5.3-2025-03-01')).toBe( + maxTokensMap[EModelEndpoint.openAI]['gpt-5.3'], + ); + expect(getModelMaxTokens('gpt-5.3-preview')).toBe( + maxTokensMap[EModelEndpoint.openAI]['gpt-5.3'], + ); + }); + + test('should return correct tokens for gpt-5.4 matches', () => { + expect(getModelMaxTokens('gpt-5.4')).toBe(maxTokensMap[EModelEndpoint.openAI]['gpt-5.4']); + expect(getModelMaxTokens('gpt-5.4-thinking')).toBe( + maxTokensMap[EModelEndpoint.openAI]['gpt-5.4'], + ); + expect(getModelMaxTokens('openai/gpt-5.4')).toBe( + maxTokensMap[EModelEndpoint.openAI]['gpt-5.4'], + ); + }); + + test('should return correct tokens for gpt-5.4-pro matches', () => { + expect(getModelMaxTokens('gpt-5.4-pro')).toBe( + maxTokensMap[EModelEndpoint.openAI]['gpt-5.4-pro'], + ); + expect(getModelMaxTokens('openai/gpt-5.4-pro')).toBe( + maxTokensMap[EModelEndpoint.openAI]['gpt-5.4-pro'], + ); + }); + test('should return correct tokens for Anthropic models', () => { const models = [ 'claude-2.1', @@ -237,16 +270,6 @@ describe('getModelMaxTokens', () => { }); }); - // Tests for Google models - test('should return correct tokens for exact match - Google models', () => { - expect(getModelMaxTokens('text-bison-32k', EModelEndpoint.google)).toBe( - maxTokensMap[EModelEndpoint.google]['text-bison-32k'], - ); - expect(getModelMaxTokens('codechat-bison-32k', EModelEndpoint.google)).toBe( - maxTokensMap[EModelEndpoint.google]['codechat-bison-32k'], - ); - }); - test('should return undefined for no match - Google models', () => { expect(getModelMaxTokens('unknown-google-model', EModelEndpoint.google)).toBeUndefined(); }); @@ -279,6 +302,12 @@ describe('getModelMaxTokens', () => { expect(getModelMaxTokens('gemini-3', EModelEndpoint.google)).toBe( maxTokensMap[EModelEndpoint.google]['gemini-3'], ); + expect(getModelMaxTokens('gemini-3.1-pro-preview', EModelEndpoint.google)).toBe( + maxTokensMap[EModelEndpoint.google]['gemini-3.1'], + ); + expect(getModelMaxTokens('gemini-3.1-pro-preview-customtools', EModelEndpoint.google)).toBe( + maxTokensMap[EModelEndpoint.google]['gemini-3.1'], + ); expect(getModelMaxTokens('gemini-2.5-pro', EModelEndpoint.google)).toBe( maxTokensMap[EModelEndpoint.google]['gemini-2.5-pro'], ); @@ -297,12 +326,6 @@ describe('getModelMaxTokens', () => { expect(getModelMaxTokens('gemini-pro', EModelEndpoint.google)).toBe( maxTokensMap[EModelEndpoint.google]['gemini'], ); - expect(getModelMaxTokens('code-', EModelEndpoint.google)).toBe( - maxTokensMap[EModelEndpoint.google]['code-'], - ); - expect(getModelMaxTokens('chat-', EModelEndpoint.google)).toBe( - maxTokensMap[EModelEndpoint.google]['chat-'], - ); }); test('should return correct tokens for partial match - Cohere models', () => { @@ -485,8 +508,20 @@ describe('getModelMaxTokens', () => { }); test('should return correct max output tokens for GPT-5 models', () => { - const { getModelMaxOutputTokens } = require('librechat-data-provider'); - ['gpt-5', 'gpt-5-mini', 'gpt-5-nano', 'gpt-5-pro'].forEach((model) => { + const { getModelMaxOutputTokens } = require('@librechat/api'); + const gpt5Models = [ + 'gpt-5', + 'gpt-5.1', + 'gpt-5.2', + 'gpt-5.3', + 'gpt-5.4', + 'gpt-5.4-pro', + 'gpt-5-mini', + 'gpt-5-nano', + 'gpt-5-pro', + 'gpt-5.2-pro', + ]; + for (const model of gpt5Models) { expect(getModelMaxOutputTokens(model)).toBe(maxOutputTokensMap[EModelEndpoint.openAI][model]); expect(getModelMaxOutputTokens(model, EModelEndpoint.openAI)).toBe( maxOutputTokensMap[EModelEndpoint.openAI][model], @@ -494,7 +529,7 @@ describe('getModelMaxTokens', () => { expect(getModelMaxOutputTokens(model, EModelEndpoint.azureOpenAI)).toBe( maxOutputTokensMap[EModelEndpoint.azureOpenAI][model], ); - }); + } }); test('should return correct max output tokens for GPT-OSS models', () => { @@ -511,6 +546,184 @@ describe('getModelMaxTokens', () => { }); }); +describe('findMatchingPattern - longest match wins', () => { + test('should prefer longer matching key over shorter cross-provider pattern', () => { + const result = findMatchingPattern( + 'gpt-5.2-chat-2025-12-11', + maxTokensMap[EModelEndpoint.openAI], + ); + expect(result).toBe('gpt-5.2'); + }); + + test('should match gpt-5.2 tokens for date-suffixed chat variant', () => { + expect(getModelMaxTokens('gpt-5.2-chat-2025-12-11')).toBe( + maxTokensMap[EModelEndpoint.openAI]['gpt-5.2'], + ); + }); + + test('should match gpt-5.2-pro over shorter patterns', () => { + expect(getModelMaxTokens('gpt-5.2-pro-chat-2025-12-11')).toBe( + maxTokensMap[EModelEndpoint.openAI]['gpt-5.2-pro'], + ); + }); + + test('should match gpt-5-mini over gpt-5 for mini variants', () => { + expect(getModelMaxTokens('gpt-5-mini-chat-2025-01-01')).toBe( + maxTokensMap[EModelEndpoint.openAI]['gpt-5-mini'], + ); + }); + + test('should prefer gpt-4-1106 over gpt-4 for versioned model names', () => { + const result = findMatchingPattern('gpt-4-1106-preview', maxTokensMap[EModelEndpoint.openAI]); + expect(result).toBe('gpt-4-1106'); + }); + + test('should prefer gpt-4-32k-0613 over gpt-4-32k for exact versioned names', () => { + const result = findMatchingPattern('gpt-4-32k-0613', maxTokensMap[EModelEndpoint.openAI]); + expect(result).toBe('gpt-4-32k-0613'); + }); + + test('should prefer claude-3-5-sonnet over claude-3', () => { + const result = findMatchingPattern( + 'claude-3-5-sonnet-20241022', + maxTokensMap[EModelEndpoint.anthropic], + ); + expect(result).toBe('claude-3-5-sonnet'); + }); + + test('should prefer gemini-2.0-flash-lite over gemini-2.0-flash', () => { + const result = findMatchingPattern( + 'gemini-2.0-flash-lite-preview', + maxTokensMap[EModelEndpoint.google], + ); + expect(result).toBe('gemini-2.0-flash-lite'); + }); +}); + +describe('findMatchingPattern - bestLength selection', () => { + test('should return the longest matching key when multiple keys match', () => { + const tokensMap = { short: 100, 'short-med': 200, 'short-med-long': 300 }; + expect(findMatchingPattern('short-med-long-extra', tokensMap)).toBe('short-med-long'); + }); + + test('should return the longest match regardless of key insertion order', () => { + const tokensMap = { 'a-b-c': 300, a: 100, 'a-b': 200 }; + expect(findMatchingPattern('a-b-c-d', tokensMap)).toBe('a-b-c'); + }); + + test('should return null when no key matches', () => { + const tokensMap = { alpha: 100, beta: 200 }; + expect(findMatchingPattern('gamma-delta', tokensMap)).toBeNull(); + }); + + test('should return the single matching key when only one matches', () => { + const tokensMap = { alpha: 100, beta: 200, gamma: 300 }; + expect(findMatchingPattern('beta-extended', tokensMap)).toBe('beta'); + }); + + test('should match case-insensitively against model name', () => { + const tokensMap = { 'gpt-5': 400000 }; + expect(findMatchingPattern('GPT-5-turbo', tokensMap)).toBe('gpt-5'); + }); + + test('should select the longest key among overlapping substring matches', () => { + const tokensMap = { 'gpt-': 100, 'gpt-5': 200, 'gpt-5.2': 300, 'gpt-5.2-pro': 400 }; + expect(findMatchingPattern('gpt-5.2-pro-2025-01-01', tokensMap)).toBe('gpt-5.2-pro'); + expect(findMatchingPattern('gpt-5.2-chat-2025-01-01', tokensMap)).toBe('gpt-5.2'); + expect(findMatchingPattern('gpt-5.1-preview', tokensMap)).toBe('gpt-5'); + expect(findMatchingPattern('gpt-unknown', tokensMap)).toBe('gpt-'); + }); + + test('should not be confused by a short key that appears later in the model name', () => { + const tokensMap = { 'model-v2': 200, v2: 100 }; + expect(findMatchingPattern('model-v2-extended', tokensMap)).toBe('model-v2'); + }); + + test('should handle exact-length match as the best match', () => { + const tokensMap = { 'exact-model': 500, exact: 100 }; + expect(findMatchingPattern('exact-model', tokensMap)).toBe('exact-model'); + }); + + test('should return null for empty model name', () => { + expect(findMatchingPattern('', { 'gpt-5': 400000 })).toBeNull(); + }); + + test('should prefer last-defined key on same-length ties', () => { + const tokensMap = { 'aa-bb': 100, 'cc-dd': 200 }; + // model name contains both 5-char keys; last-defined wins in reverse iteration + expect(findMatchingPattern('aa-bb-cc-dd', tokensMap)).toBe('cc-dd'); + }); + + test('longest match beats short cross-provider pattern even when both present', () => { + const tokensMap = { 'gpt-5.2': 400000, 'chat-': 8187 }; + expect(findMatchingPattern('gpt-5.2-chat-2025-12-11', tokensMap)).toBe('gpt-5.2'); + }); + + test('should match case-insensitively against keys', () => { + const tokensMap = { 'GPT-5': 400000 }; + expect(findMatchingPattern('gpt-5-turbo', tokensMap)).toBe('GPT-5'); + }); +}); + +describe('findMatchingPattern - iteration performance', () => { + let includesSpy; + + beforeEach(() => { + includesSpy = jest.spyOn(String.prototype, 'includes'); + }); + + afterEach(() => { + includesSpy.mockRestore(); + }); + + test('exact match early-exits with minimal includes() checks', () => { + const openAIMap = maxTokensMap[EModelEndpoint.openAI]; + const keys = Object.keys(openAIMap); + const lastKey = keys[keys.length - 1]; + includesSpy.mockClear(); + const result = findMatchingPattern(lastKey, openAIMap); + const exactCalls = includesSpy.mock.calls.length; + + expect(result).toBe(lastKey); + expect(exactCalls).toBe(1); + }); + + test('bestLength check skips includes() for shorter keys after a long match', () => { + const openAIMap = maxTokensMap[EModelEndpoint.openAI]; + includesSpy.mockClear(); + findMatchingPattern('gpt-3.5-turbo-0301-test', openAIMap); + const longKeyCalls = includesSpy.mock.calls.length; + + includesSpy.mockClear(); + findMatchingPattern('gpt-5.3-chat-latest', openAIMap); + const shortKeyCalls = includesSpy.mock.calls.length; + + // gpt-3.5-turbo-0301 (20 chars) matches early, then bestLength prunes most keys + // gpt-5.3 (7 chars) is short, so fewer keys are pruned by the length check + expect(longKeyCalls).toBeLessThan(shortKeyCalls); + }); + + test('last-defined keys are checked first in reverse iteration', () => { + const tokensMap = { first: 100, second: 200, third: 300 }; + includesSpy.mockClear(); + const result = findMatchingPattern('third', tokensMap); + const calls = includesSpy.mock.calls.length; + + // 'third' is last key, found on first reverse check, exact match exits immediately + expect(result).toBe('third'); + expect(calls).toBe(1); + }); +}); + +describe('deprecated PaLM2/Codey model removal', () => { + test('deprecated PaLM2/Codey models no longer have token entries', () => { + expect(getModelMaxTokens('text-bison-32k', EModelEndpoint.google)).toBeUndefined(); + expect(getModelMaxTokens('codechat-bison-32k', EModelEndpoint.google)).toBeUndefined(); + expect(getModelMaxTokens('code-bison', EModelEndpoint.google)).toBeUndefined(); + expect(getModelMaxTokens('chat-bison', EModelEndpoint.google)).toBeUndefined(); + }); +}); + describe('matchModelName', () => { it('should return the exact model name if it exists in maxTokensMap', () => { expect(matchModelName('gpt-4-32k-0613')).toBe('gpt-4-32k-0613'); @@ -606,10 +819,16 @@ describe('matchModelName', () => { expect(matchModelName('gpt-5-pro-2025-01-30-0130')).toBe('gpt-5-pro'); }); - // Tests for Google models - it('should return the exact model name if it exists in maxTokensMap - Google models', () => { - expect(matchModelName('text-bison-32k', EModelEndpoint.google)).toBe('text-bison-32k'); - expect(matchModelName('codechat-bison-32k', EModelEndpoint.google)).toBe('codechat-bison-32k'); + it('should return the closest matching key for gpt-5.3 matches', () => { + expect(matchModelName('openai/gpt-5.3')).toBe('gpt-5.3'); + expect(matchModelName('gpt-5.3-codex')).toBe('gpt-5.3'); + expect(matchModelName('gpt-5.3-2025-03-01')).toBe('gpt-5.3'); + }); + + it('should return the closest matching key for gpt-5.4 matches', () => { + expect(matchModelName('openai/gpt-5.4')).toBe('gpt-5.4'); + expect(matchModelName('gpt-5.4-thinking')).toBe('gpt-5.4'); + expect(matchModelName('gpt-5.4-pro')).toBe('gpt-5.4-pro'); }); it('should return the input model name if no match is found - Google models', () => { @@ -617,11 +836,6 @@ describe('matchModelName', () => { 'unknown-google-model', ); }); - - it('should return the closest matching key for partial matches - Google models', () => { - expect(matchModelName('code-', EModelEndpoint.google)).toBe('code-'); - expect(matchModelName('chat-', EModelEndpoint.google)).toBe('chat-'); - }); }); describe('Meta Models Tests', () => { @@ -1162,6 +1376,56 @@ describe('Claude Model Tests', () => { expect(matchModelName(model, EModelEndpoint.anthropic)).toBe('claude-opus-4-6'); }); }); + + it('should return correct context length for Claude Sonnet 4.6 (1M)', () => { + expect(getModelMaxTokens('claude-sonnet-4-6', EModelEndpoint.anthropic)).toBe( + maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'], + ); + expect(getModelMaxTokens('claude-sonnet-4-6')).toBe( + maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'], + ); + }); + + it('should return correct max output tokens for Claude Sonnet 4.6 (64K)', () => { + const { getModelMaxOutputTokens } = require('@librechat/api'); + expect(getModelMaxOutputTokens('claude-sonnet-4-6', EModelEndpoint.anthropic)).toBe( + maxOutputTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'], + ); + }); + + it('should handle Claude Sonnet 4.6 model name variations', () => { + const modelVariations = [ + 'claude-sonnet-4-6', + 'claude-sonnet-4-6-20260101', + 'claude-sonnet-4-6-latest', + 'anthropic/claude-sonnet-4-6', + 'claude-sonnet-4-6/anthropic', + 'claude-sonnet-4-6-preview', + ]; + + modelVariations.forEach((model) => { + const modelKey = findMatchingPattern(model, maxTokensMap[EModelEndpoint.anthropic]); + expect(modelKey).toBe('claude-sonnet-4-6'); + expect(getModelMaxTokens(model, EModelEndpoint.anthropic)).toBe( + maxTokensMap[EModelEndpoint.anthropic]['claude-sonnet-4-6'], + ); + }); + }); + + it('should match model names correctly for Claude Sonnet 4.6', () => { + const modelVariations = [ + 'claude-sonnet-4-6', + 'claude-sonnet-4-6-20260101', + 'claude-sonnet-4-6-latest', + 'anthropic/claude-sonnet-4-6', + 'claude-sonnet-4-6/anthropic', + 'claude-sonnet-4-6-preview', + ]; + + modelVariations.forEach((model) => { + expect(matchModelName(model, EModelEndpoint.anthropic)).toBe('claude-sonnet-4-6'); + }); + }); }); describe('Moonshot/Kimi Model Tests', () => { diff --git a/bun.lock b/bun.lock index 600a640c87..f6e3228519 100644 --- a/bun.lock +++ b/bun.lock @@ -7,7 +7,7 @@ "devDependencies": { "@axe-core/playwright": "^4.10.1", "@eslint/compat": "^1.2.6", - "@eslint/eslintrc": "^3.3.1", + "@eslint/eslintrc": "^3.3.4", "@eslint/js": "^9.20.0", "@playwright/test": "^1.56.1", "@types/react-virtualized": "^9.22.0", @@ -31,30 +31,33 @@ "lint-staged": "^15.4.3", "prettier": "^3.5.0", "prettier-plugin-tailwindcss": "^0.6.11", + "turbo": "^2.8.12", "typescript-eslint": "^8.24.0", }, }, "api": { "name": "@librechat/backend", - "version": "0.8.2-rc2", + "version": "0.8.4-rc1", "dependencies": { - "@aws-sdk/client-bedrock-runtime": "^3.941.0", - "@aws-sdk/client-s3": "^3.758.0", + "@anthropic-ai/vertex-sdk": "^0.14.3", + "@aws-sdk/client-bedrock-runtime": "^3.980.0", + "@aws-sdk/client-s3": "^3.980.0", "@aws-sdk/s3-request-presigner": "^3.758.0", "@azure/identity": "^4.7.0", "@azure/search-documents": "^12.0.0", - "@azure/storage-blob": "^12.27.0", - "@googleapis/youtube": "^20.0.0", + "@azure/storage-blob": "^12.30.0", + "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", - "@langchain/core": "^0.3.79", - "@librechat/agents": "^3.0.50", + "@langchain/core": "^0.3.80", + "@librechat/agents": "^3.1.56", "@librechat/api": "*", "@librechat/data-schemas": "*", "@microsoft/microsoft-graph-client": "^3.0.7", - "@modelcontextprotocol/sdk": "^1.24.3", + "@modelcontextprotocol/sdk": "^1.27.1", "@node-saml/passport-saml": "^5.1.0", "@smithy/node-http-handler": "^4.4.5", - "axios": "^1.12.1", + "ai-tokenizer": "^1.0.6", + "axios": "^1.13.5", "bcryptjs": "^2.4.3", "compression": "^1.8.1", "connect-redis": "^8.1.0", @@ -64,12 +67,12 @@ "dedent": "^1.5.3", "dotenv": "^16.0.3", "eventsource": "^3.0.2", - "express": "^5.1.0", + "express": "^5.2.1", "express-mongo-sanitize": "^2.2.0", - "express-rate-limit": "^8.2.1", + "express-rate-limit": "^8.3.0", "express-session": "^1.18.2", "express-static-gzip": "^2.2.0", - "file-type": "^18.7.0", + "file-type": "^21.3.2", "firebase": "^11.0.2", "form-data": "^4.0.4", "handlebars": "^4.7.7", @@ -82,13 +85,15 @@ "keyv-file": "^5.1.2", "klona": "^2.0.6", "librechat-data-provider": "*", - "lodash": "^4.17.21", + "lodash": "^4.17.23", + "mammoth": "^1.11.0", + "mathjs": "^15.1.0", "meilisearch": "^0.38.0", "memorystore": "^1.6.7", "mime": "^3.0.0", "module-alias": "^2.2.3", "mongoose": "^8.12.1", - "multer": "^2.0.2", + "multer": "^2.1.1", "nanoid": "^3.3.7", "node-fetch": "^2.7.0", "nodemailer": "^7.0.11", @@ -104,15 +109,15 @@ "passport-jwt": "^4.0.1", "passport-ldapauth": "^3.0.1", "passport-local": "^1.0.0", + "pdfjs-dist": "^5.4.624", "rate-limit-redis": "^4.2.0", "sharp": "^0.33.5", - "tiktoken": "^1.0.15", "traverse": "^0.6.7", "ua-parser-js": "^1.0.36", - "undici": "^7.10.0", + "undici": "^7.24.1", "winston": "^3.11.0", "winston-daily-rotate-file": "^5.0.0", - "youtube-transcript": "^1.2.1", + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", "zod": "^3.22.4", }, "devDependencies": { @@ -124,7 +129,7 @@ }, "client": { "name": "@librechat/frontend", - "version": "0.8.2-rc2", + "version": "0.8.4-rc1", "dependencies": { "@ariakit/react": "^0.4.15", "@ariakit/react-core": "^0.4.17", @@ -135,11 +140,12 @@ "@librechat/client": "*", "@marsidev/react-turnstile": "^1.1.0", "@mcp-ui/client": "^5.7.0", + "@monaco-editor/react": "^4.7.0", "@radix-ui/react-accordion": "^1.1.2", - "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-alert-dialog": "1.0.2", "@radix-ui/react-checkbox": "^1.0.3", "@radix-ui/react-collapsible": "^1.0.3", - "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dialog": "1.0.2", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-hover-card": "^1.0.5", "@radix-ui/react-icons": "^1.3.0", @@ -174,9 +180,10 @@ "jotai": "^2.12.5", "js-cookie": "^3.0.5", "librechat-data-provider": "*", - "lodash": "^4.17.21", + "lodash": "^4.17.23", "lucide-react": "^0.394.0", "match-sorter": "^8.1.0", + "mermaid": "^11.13.0", "micromark-extension-llm-math": "^3.1.0", "qrcode.react": "^4.2.0", "rc-input-number": "^7.4.2", @@ -189,10 +196,9 @@ "react-gtm-module": "^2.0.11", "react-hook-form": "^7.43.9", "react-i18next": "^15.4.0", - "react-lazy-load-image-component": "^1.6.0", "react-markdown": "^9.0.1", "react-resizable-panels": "^3.0.6", - "react-router-dom": "^6.11.2", + "react-router-dom": "^6.30.3", "react-speech-recognition": "^3.10.0", "react-textarea-autosize": "^8.4.0", "react-transition-group": "^4.4.5", @@ -206,9 +212,11 @@ "remark-math": "^6.0.0", "remark-supersub": "^1.0.0", "sse.js": "^2.5.0", + "swr": "^2.3.8", "tailwind-merge": "^1.9.1", "tailwindcss-animate": "^1.0.5", "tailwindcss-radix": "^2.8.0", + "ts-md5": "^1.3.1", "zod": "^3.22.4", }, "devDependencies": { @@ -216,6 +224,7 @@ "@babel/preset-env": "^7.22.15", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.22.15", + "@happy-dom/jest-environment": "^20.8.3", "@tanstack/react-query-devtools": "^4.29.0", "@testing-library/dom": "^9.3.0", "@testing-library/jest-dom": "^5.16.5", @@ -224,10 +233,10 @@ "@types/jest": "^29.5.14", "@types/js-cookie": "^3.0.6", "@types/lodash": "^4.17.15", - "@types/node": "^20.3.0", + "@types/node": "^20.19.35", "@types/react": "^18.2.11", "@types/react-dom": "^18.2.4", - "@vitejs/plugin-react": "^4.3.4", + "@vitejs/plugin-react": "^5.1.4", "autoprefixer": "^10.4.20", "babel-plugin-replace-ts-export-assignment": "^0.0.2", "babel-plugin-root-import": "^6.6.0", @@ -238,23 +247,23 @@ "identity-obj-proxy": "^3.0.0", "jest": "^30.2.0", "jest-canvas-mock": "^2.5.2", - "jest-environment-jsdom": "^29.7.0", + "jest-environment-jsdom": "^30.2.0", "jest-file-loader": "^1.0.3", "jest-junit": "^16.0.0", + "monaco-editor": "^0.55.1", "postcss": "^8.4.31", - "postcss-loader": "^7.1.0", - "postcss-preset-env": "^8.2.0", + "postcss-preset-env": "^11.2.0", "tailwindcss": "^3.4.1", "typescript": "^5.3.3", - "vite": "^6.4.1", + "vite": "^7.3.1", "vite-plugin-compression2": "^2.2.1", - "vite-plugin-node-polyfills": "^0.23.0", - "vite-plugin-pwa": "^0.21.2", + "vite-plugin-node-polyfills": "^0.25.0", + "vite-plugin-pwa": "^1.2.0", }, }, "packages/api": { "name": "@librechat/api", - "version": "1.7.22", + "version": "1.7.26", "devDependencies": { "@babel/preset-env": "^7.21.5", "@babel/preset-react": "^7.18.6", @@ -266,7 +275,6 @@ "@rollup/plugin-replace": "^5.0.5", "@rollup/plugin-typescript": "^12.1.2", "@types/bun": "^1.2.15", - "@types/diff": "^6.0.0", "@types/express": "^5.0.0", "@types/express-session": "^1.18.2", "@types/jest": "^29.5.2", @@ -279,49 +287,60 @@ "jest": "^30.2.0", "jest-junit": "^16.0.0", "librechat-data-provider": "*", + "mammoth": "^1.11.0", "mongodb": "^6.14.2", - "rimraf": "^6.1.2", - "rollup": "^4.22.4", + "pdfjs-dist": "^5.4.624", + "rimraf": "^6.1.3", + "rollup": "^4.34.9", "rollup-plugin-peer-deps-external": "^2.2.4", "ts-node": "^10.9.2", "typescript": "^5.0.4", + "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", }, "peerDependencies": { - "@aws-sdk/client-s3": "^3.758.0", + "@anthropic-ai/vertex-sdk": "^0.14.3", + "@aws-sdk/client-bedrock-runtime": "^3.970.0", + "@aws-sdk/client-s3": "^3.980.0", "@azure/identity": "^4.7.0", "@azure/search-documents": "^12.0.0", - "@azure/storage-blob": "^12.27.0", + "@azure/storage-blob": "^12.30.0", + "@google/genai": "^1.19.0", "@keyv/redis": "^4.3.3", - "@langchain/core": "^0.3.79", - "@librechat/agents": "^3.0.50", + "@langchain/core": "^0.3.80", + "@librechat/agents": "^3.1.56", "@librechat/data-schemas": "*", - "@modelcontextprotocol/sdk": "^1.24.3", - "axios": "^1.12.1", + "@modelcontextprotocol/sdk": "^1.27.1", + "@smithy/node-http-handler": "^4.4.5", + "ai-tokenizer": "^1.0.6", + "axios": "^1.13.5", "connect-redis": "^8.1.0", - "diff": "^7.0.0", "eventsource": "^3.0.2", "express": "^5.1.0", "express-session": "^1.18.2", "firebase": "^11.0.2", "form-data": "^4.0.4", + "google-auth-library": "^9.15.1", + "https-proxy-agent": "^7.0.6", "ioredis": "^5.3.2", "js-yaml": "^4.1.1", "jsonwebtoken": "^9.0.0", "keyv": "^5.3.2", "keyv-file": "^5.1.2", "librechat-data-provider": "*", + "mammoth": "^1.11.0", + "mathjs": "^15.1.0", "memorystore": "^1.6.7", "mongoose": "^8.12.1", "node-fetch": "2.7.0", + "pdfjs-dist": "^5.4.624", "rate-limit-redis": "^4.2.0", - "tiktoken": "^1.0.15", - "undici": "^7.10.0", + "undici": "^7.24.1", "zod": "^3.22.4", }, }, "packages/client": { "name": "@librechat/client", - "version": "0.4.51", + "version": "0.4.55", "devDependencies": { "@babel/core": "^7.28.5", "@babel/preset-env": "^7.28.5", @@ -351,8 +370,8 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-i18next": "^15.4.0", - "rimraf": "^6.1.2", - "rollup": "^4.0.0", + "rimraf": "^6.1.3", + "rollup": "^4.34.9", "rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-typescript2": "^0.35.0", @@ -366,10 +385,10 @@ "@dicebear/core": "^9.2.2", "@headlessui/react": "^2.1.2", "@radix-ui/react-accordion": "^1.2.11", - "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-alert-dialog": "1.0.2", "@radix-ui/react-checkbox": "^1.0.3", "@radix-ui/react-collapsible": "^1.1.11", - "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dialog": "1.0.2", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-hover-card": "^1.0.5", "@radix-ui/react-icons": "^1.3.0", @@ -409,9 +428,9 @@ }, "packages/data-provider": { "name": "librechat-data-provider", - "version": "0.8.231", + "version": "0.8.400", "dependencies": { - "axios": "^1.12.1", + "axios": "^1.13.5", "dayjs": "^1.11.13", "js-yaml": "^4.1.1", "zod": "^3.22.4", @@ -420,7 +439,6 @@ "@babel/preset-env": "^7.21.5", "@babel/preset-react": "^7.18.6", "@babel/preset-typescript": "^7.21.0", - "@langchain/core": "^0.3.62", "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", @@ -435,8 +453,8 @@ "jest": "^30.2.0", "jest-junit": "^16.0.0", "openapi-types": "^12.1.3", - "rimraf": "^6.1.2", - "rollup": "^4.22.4", + "rimraf": "^6.1.3", + "rollup": "^4.34.9", "rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-typescript2": "^0.35.0", "typescript": "^5.0.4", @@ -447,7 +465,7 @@ }, "packages/data-schemas": { "name": "@librechat/data-schemas", - "version": "0.0.35", + "version": "0.0.39", "devDependencies": { "@rollup/plugin-alias": "^5.1.0", "@rollup/plugin-commonjs": "^29.0.0", @@ -456,15 +474,14 @@ "@rollup/plugin-replace": "^5.0.5", "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^12.1.2", - "@types/diff": "^6.0.0", "@types/express": "^5.0.0", "@types/jest": "^29.5.2", "@types/node": "^20.3.0", "jest": "^30.2.0", "jest-junit": "^16.0.0", "mongodb-memory-server": "^10.1.4", - "rimraf": "^6.1.2", - "rollup": "^4.22.4", + "rimraf": "^6.1.3", + "rollup": "^4.34.9", "rollup-plugin-peer-deps-external": "^2.2.4", "rollup-plugin-typescript2": "^0.35.0", "ts-node": "^10.9.2", @@ -474,7 +491,7 @@ "jsonwebtoken": "^9.0.2", "klona": "^2.0.6", "librechat-data-provider": "*", - "lodash": "^4.17.21", + "lodash": "^4.17.23", "meilisearch": "^0.38.0", "mongoose": "^8.12.1", "nanoid": "^3.3.7", @@ -484,11 +501,20 @@ }, }, "overrides": { + "@anthropic-ai/sdk": "0.73.0", + "@hono/node-server": "^1.19.10", "axios": "1.12.1", "elliptic": "^6.6.1", + "fast-xml-parser": "5.3.8", "form-data": "^4.0.4", + "hono": "^4.12.4", "katex": "^0.16.21", + "langsmith": "0.4.12", "mdast-util-gfm-autolink-literal": "2.0.0", + "serialize-javascript": "^7.0.3", + "svgo": "^2.8.2", + "tslib": "^2.8.1", + "underscore": "1.13.8", }, "packages": { "@aashutoshrathi/word-wrap": ["@aashutoshrathi/word-wrap@1.2.6", "", {}, "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA=="], @@ -497,7 +523,11 @@ "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], - "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.65.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-zIdPOcrCVEI8t3Di40nH4z9EoeyGZfXbYSvWdDLsB/KkaSYMnEgC7gmcgWu83g2NTn1ZTpbMvpdttWDGGIk6zw=="], + "@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="], + + "@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.73.0", "", { "dependencies": { "json-schema-to-ts": "^3.1.1" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "optionalPeers": ["zod"], "bin": { "anthropic-ai-sdk": "bin/cli" } }, "sha512-URURVzhxXGJDGUGFunIOtBlSl7KWvZiAAKY/ttTkZAkXT9bTPqdk2eK0b8qqSxXpikh3QKPnPYpiyX98zf5ebw=="], + + "@anthropic-ai/vertex-sdk": ["@anthropic-ai/vertex-sdk@0.14.4", "", { "dependencies": { "@anthropic-ai/sdk": ">=0.50.3 <1", "google-auth-library": "^9.4.2" } }, "sha512-BZUPRWghZxfSFtAxU563wH+jfWBPoedAwsVxG35FhmNsjeV8tyfN+lFriWhCpcZApxA4NdT6Soov+PzfnxxD5g=="], "@apideck/better-ajv-errors": ["@apideck/better-ajv-errors@0.3.6", "", { "dependencies": { "json-schema": "^0.4.0", "jsonpointer": "^5.0.0", "leven": "^3.1.0" }, "peerDependencies": { "ajv": ">=8" } }, "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA=="], @@ -525,19 +555,21 @@ "@aws-sdk/client-bedrock-agent-runtime": ["@aws-sdk/client-bedrock-agent-runtime@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/credential-provider-node": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/eventstream-serde-browser": "^4.2.4", "@smithy/eventstream-serde-config-resolver": "^4.3.4", "@smithy/eventstream-serde-node": "^4.2.4", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-k2UeG/+Ka74jztHDzYNrpNLDSsMCst+ph3+e7uAX5Jmo40tVKa+sVu4DkV3BIXuktc6jqM1ewtfPNug79kN6JQ=="], - "@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-node": "3.952.0", "@aws-sdk/eventstream-handler-node": "3.936.0", "@aws-sdk/middleware-eventstream": "3.936.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/middleware-websocket": "3.936.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/token-providers": "3.952.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Xc1xqIz/OdFd23UQ6cvROD+3tfvDpp5dabMqUYXFiKlk5psMNM9xhzLwWK7DE1tr1ra/dui77w8JOiLA1dC7AA=="], + "@aws-sdk/client-bedrock-runtime": ["@aws-sdk/client-bedrock-runtime@3.1004.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.18", "@aws-sdk/credential-provider-node": "^3.972.18", "@aws-sdk/eventstream-handler-node": "^3.972.10", "@aws-sdk/middleware-eventstream": "^3.972.7", "@aws-sdk/middleware-host-header": "^3.972.7", "@aws-sdk/middleware-logger": "^3.972.7", "@aws-sdk/middleware-recursion-detection": "^3.972.7", "@aws-sdk/middleware-user-agent": "^3.972.19", "@aws-sdk/middleware-websocket": "^3.972.12", "@aws-sdk/region-config-resolver": "^3.972.7", "@aws-sdk/token-providers": "3.1004.0", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@aws-sdk/util-user-agent-browser": "^3.972.7", "@aws-sdk/util-user-agent-node": "^3.973.4", "@smithy/config-resolver": "^4.4.10", "@smithy/core": "^3.23.8", "@smithy/eventstream-serde-browser": "^4.2.11", "@smithy/eventstream-serde-config-resolver": "^4.3.11", "@smithy/eventstream-serde-node": "^4.2.11", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/hash-node": "^4.2.11", "@smithy/invalid-dependency": "^4.2.11", "@smithy/middleware-content-length": "^4.2.11", "@smithy/middleware-endpoint": "^4.4.22", "@smithy/middleware-retry": "^4.4.39", "@smithy/middleware-serde": "^4.2.12", "@smithy/middleware-stack": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/node-http-handler": "^4.4.14", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.38", "@smithy/util-defaults-mode-node": "^4.2.41", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-t8cl+bPLlHZQD2Sw1a4hSLUybqJZU71+m8znkyeU8CHntFqEp2mMbuLKdHKaAYQ1fAApXMsvzenCAkDzNeeJlw=="], "@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.623.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/client-sso-oidc": "3.623.0", "@aws-sdk/core": "3.623.0", "@aws-sdk/credential-provider-node": "3.623.0", "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-user-agent": "3.620.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", "@smithy/core": "^2.3.2", "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", "@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", "@smithy/node-http-handler": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", "@smithy/util-defaults-mode-browser": "^3.0.14", "@smithy/util-defaults-mode-node": "^3.0.14", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-kGYnTzXTMGdjko5+GZ1PvWvfXA7quiOp5iMo5gbh5b55pzIdc918MHN0pvaqplVGWYlaFJF4YzxUT5Nbxd7Xeg=="], "@aws-sdk/client-kendra": ["@aws-sdk/client-kendra@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/credential-provider-node": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-DWyNlC6BFhzoDkyKZ3xv0BC/xcXF3Tpq6j6Z42DXO9KEUjiGmC3se9l/GFEVtRLh/DR4p7cTJsxzA2QNuthRNg=="], - "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.758.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/credential-provider-node": "3.758.0", "@aws-sdk/middleware-bucket-endpoint": "3.734.0", "@aws-sdk/middleware-expect-continue": "3.734.0", "@aws-sdk/middleware-flexible-checksums": "3.758.0", "@aws-sdk/middleware-host-header": "3.734.0", "@aws-sdk/middleware-location-constraint": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.734.0", "@aws-sdk/middleware-sdk-s3": "3.758.0", "@aws-sdk/middleware-ssec": "3.734.0", "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/signature-v4-multi-region": "3.758.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", "@aws-sdk/util-user-agent-node": "3.758.0", "@aws-sdk/xml-builder": "3.734.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/eventstream-serde-browser": "^4.0.1", "@smithy/eventstream-serde-config-resolver": "^4.0.1", "@smithy/eventstream-serde-node": "^4.0.1", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/hash-blob-browser": "^4.0.1", "@smithy/hash-node": "^4.0.1", "@smithy/hash-stream-node": "^4.0.1", "@smithy/invalid-dependency": "^4.0.1", "@smithy/md5-js": "^4.0.1", "@smithy/middleware-content-length": "^4.0.1", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-retry": "^4.0.7", "@smithy/middleware-serde": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.7", "@smithy/util-defaults-mode-node": "^4.0.7", "@smithy/util-endpoints": "^3.0.1", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.2", "tslib": "^2.6.2" } }, "sha512-f8SlhU9/93OC/WEI6xVJf/x/GoQFj9a/xXK6QCtr5fvCjfSLgMVFmKTiIl/tgtDRzxUDc8YS6EGtbHjJ3Y/atg=="], + "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.1004.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.18", "@aws-sdk/credential-provider-node": "^3.972.18", "@aws-sdk/middleware-bucket-endpoint": "^3.972.7", "@aws-sdk/middleware-expect-continue": "^3.972.7", "@aws-sdk/middleware-flexible-checksums": "^3.973.4", "@aws-sdk/middleware-host-header": "^3.972.7", "@aws-sdk/middleware-location-constraint": "^3.972.7", "@aws-sdk/middleware-logger": "^3.972.7", "@aws-sdk/middleware-recursion-detection": "^3.972.7", "@aws-sdk/middleware-sdk-s3": "^3.972.18", "@aws-sdk/middleware-ssec": "^3.972.7", "@aws-sdk/middleware-user-agent": "^3.972.19", "@aws-sdk/region-config-resolver": "^3.972.7", "@aws-sdk/signature-v4-multi-region": "^3.996.6", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@aws-sdk/util-user-agent-browser": "^3.972.7", "@aws-sdk/util-user-agent-node": "^3.973.4", "@smithy/config-resolver": "^4.4.10", "@smithy/core": "^3.23.8", "@smithy/eventstream-serde-browser": "^4.2.11", "@smithy/eventstream-serde-config-resolver": "^4.3.11", "@smithy/eventstream-serde-node": "^4.2.11", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/hash-blob-browser": "^4.2.12", "@smithy/hash-node": "^4.2.11", "@smithy/hash-stream-node": "^4.2.11", "@smithy/invalid-dependency": "^4.2.11", "@smithy/md5-js": "^4.2.11", "@smithy/middleware-content-length": "^4.2.11", "@smithy/middleware-endpoint": "^4.4.22", "@smithy/middleware-retry": "^4.4.39", "@smithy/middleware-serde": "^4.2.12", "@smithy/middleware-stack": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/node-http-handler": "^4.4.14", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.38", "@smithy/util-defaults-mode-node": "^4.2.41", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "@smithy/util-waiter": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-m0zNfpsona9jQdX1cHtHArOiuvSGZPsgp/KRZS2YjJhKah96G2UN3UNGZQ6aVjXIQjCY6UanCJo0uW9Xf2U41w=="], "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.623.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.623.0", "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-user-agent": "3.620.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", "@smithy/core": "^2.3.2", "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", "@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", "@smithy/node-http-handler": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", "@smithy/util-defaults-mode-browser": "^3.0.14", "@smithy/util-defaults-mode-node": "^3.0.14", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-oEACriysQMnHIVcNp7TD6D1nzgiHfYK0tmMBMbUxgoFuCBkW9g9QYvspHN+S9KgoePfMEXHuPUe9mtG9AH9XeA=="], "@aws-sdk/client-sso-oidc": ["@aws-sdk/client-sso-oidc@3.623.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.623.0", "@aws-sdk/credential-provider-node": "3.623.0", "@aws-sdk/middleware-host-header": "3.620.0", "@aws-sdk/middleware-logger": "3.609.0", "@aws-sdk/middleware-recursion-detection": "3.620.0", "@aws-sdk/middleware-user-agent": "3.620.0", "@aws-sdk/region-config-resolver": "3.614.0", "@aws-sdk/types": "3.609.0", "@aws-sdk/util-endpoints": "3.614.0", "@aws-sdk/util-user-agent-browser": "3.609.0", "@aws-sdk/util-user-agent-node": "3.614.0", "@smithy/config-resolver": "^3.0.5", "@smithy/core": "^2.3.2", "@smithy/fetch-http-handler": "^3.2.4", "@smithy/hash-node": "^3.0.3", "@smithy/invalid-dependency": "^3.0.3", "@smithy/middleware-content-length": "^3.0.5", "@smithy/middleware-endpoint": "^3.1.0", "@smithy/middleware-retry": "^3.0.14", "@smithy/middleware-serde": "^3.0.3", "@smithy/middleware-stack": "^3.0.3", "@smithy/node-config-provider": "^3.1.4", "@smithy/node-http-handler": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "@smithy/util-base64": "^3.0.0", "@smithy/util-body-length-browser": "^3.0.0", "@smithy/util-body-length-node": "^3.0.0", "@smithy/util-defaults-mode-browser": "^3.0.14", "@smithy/util-defaults-mode-node": "^3.0.14", "@smithy/util-endpoints": "^2.0.5", "@smithy/util-middleware": "^3.0.3", "@smithy/util-retry": "^3.0.3", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-lMFEXCa6ES/FGV7hpyrppT1PiAkqQb51AbG0zVU3TIgI2IO4XX02uzMUXImRSRqRpGymRCbJCaCs9LtKvS/37Q=="], - "@aws-sdk/core": ["@aws-sdk/core@3.758.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/core": "^3.1.5", "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-middleware": "^4.0.1", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg=="], + "@aws-sdk/core": ["@aws-sdk/core@3.973.18", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws-sdk/xml-builder": "^3.972.10", "@smithy/core": "^3.23.8", "@smithy/node-config-provider": "^4.3.11", "@smithy/property-provider": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/signature-v4": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-GUIlegfcK2LO1J2Y98sCJy63rQSiLiDOgVw7HiHPRqfI2vb3XozTVqemwO0VSGXp54ngCnAQz0Lf0YPCBINNxA=="], + + "@aws-sdk/crc64-nvme": ["@aws-sdk/crc64-nvme@3.972.4", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-HKZIZLbRyvzo/bXZU7Zmk6XqU+1C9DjI56xd02vwuDIxedxBEqP17t9ExhbP9QFeNq/a3l9GOcyirFXxmbDhmw=="], "@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.623.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.623.0", "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-sXU2KtWpFzIzE4iffSIUbl4mgbeN1Rta6BnuKtS3rrVrryku9akAxY//pulbsIsYfXRzOwZzULsa+cxQN00lrw=="], @@ -547,9 +579,9 @@ "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.623.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.620.1", "@aws-sdk/credential-provider-http": "3.622.0", "@aws-sdk/credential-provider-process": "3.620.1", "@aws-sdk/credential-provider-sso": "3.623.0", "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-kvXA1SwGneqGzFwRZNpESitnmaENHGFFuuTvgGwtMe7mzXWuA/LkXdbiHmdyAzOo0iByKTCD8uetuwh3CXy4Pw=="], - "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-jL9zc+e+7sZeJrHzYKK9GOjl1Ktinh0ORU3cM2uRBi7fuH/0zV9pdMN8PQnGXz0i4tJaKcZ1lrE4V0V6LB9NQg=="], + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.17", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/nested-clients": "^3.996.7", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-gf2E5b7LpKb+JX2oQsRIDxdRZjBFZt2olCGlWCdb3vBERbXIPgm2t1R5mEnwd4j0UEO/Tbg5zN2KJbHXttJqwA=="], - "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.758.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.758.0", "@aws-sdk/credential-provider-http": "3.758.0", "@aws-sdk/credential-provider-ini": "3.758.0", "@aws-sdk/credential-provider-process": "3.758.0", "@aws-sdk/credential-provider-sso": "3.758.0", "@aws-sdk/credential-provider-web-identity": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/credential-provider-imds": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-+DaMv63wiq7pJrhIQzZYMn4hSarKiizDoJRvyR7WGhnn0oQ/getX9Z0VNCV3i7lIFoLNTb7WMmQ9k7+z/uD5EQ=="], + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.18", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.16", "@aws-sdk/credential-provider-http": "^3.972.18", "@aws-sdk/credential-provider-ini": "^3.972.17", "@aws-sdk/credential-provider-process": "^3.972.16", "@aws-sdk/credential-provider-sso": "^3.972.17", "@aws-sdk/credential-provider-web-identity": "^3.972.17", "@aws-sdk/types": "^3.973.5", "@smithy/credential-provider-imds": "^4.2.11", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-ZDJa2gd1xiPg/nBDGhUlat02O8obaDEnICBAVS8qieZ0+nDfaB0Z3ec6gjZj27OqFTjnB/Q5a0GwQwb7rMVViw=="], "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.620.1", "", { "dependencies": { "@aws-sdk/types": "3.609.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg=="], @@ -559,57 +591,57 @@ "@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.623.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.623.0", "@aws-sdk/client-sso": "3.623.0", "@aws-sdk/credential-provider-cognito-identity": "3.623.0", "@aws-sdk/credential-provider-env": "3.620.1", "@aws-sdk/credential-provider-http": "3.622.0", "@aws-sdk/credential-provider-ini": "3.623.0", "@aws-sdk/credential-provider-node": "3.623.0", "@aws-sdk/credential-provider-process": "3.620.1", "@aws-sdk/credential-provider-sso": "3.623.0", "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-abtlH1hkVWAkzuOX79Q47l0ztWOV2Q7l7J4JwQgzEQm7+zCk5iUAiwqKyDzr+ByCyo4I3IWFjy+e1gBdL7rXQQ=="], - "@aws-sdk/eventstream-handler-node": ["@aws-sdk/eventstream-handler-node@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/eventstream-codec": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-4zIbhdRmol2KosIHmU31ATvNP0tkJhDlRj9GuawVJoEnMvJA1pd2U3SRdiOImJU3j8pT46VeS4YMmYxfjGHByg=="], + "@aws-sdk/eventstream-handler-node": ["@aws-sdk/eventstream-handler-node@3.972.10", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/eventstream-codec": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-g2Z9s6Y4iNh0wICaEqutgYgt/Pmhv5Ev9G3eKGFe2w9VuZDhc76vYdop6I5OocmpHV79d4TuLG+JWg5rQIVDVA=="], - "@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@aws-sdk/util-arn-parser": "3.723.0", "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-config-provider": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-etC7G18aF7KdZguW27GE/wpbrNmYLVT755EsFc8kXpZj8D6AFKxc7OuveinJmiy0bYXAMspJUWsF6CrGpOw6CQ=="], + "@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-arn-parser": "^3.972.3", "@smithy/node-config-provider": "^4.3.11", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-goX+axlJ6PQlRnzE2bQisZ8wVrlm6dXJfBzMJhd8LhAIBan/w1Kl73fJnalM/S+18VnpzIHumyV6DtgmvqG5IA=="], - "@aws-sdk/middleware-eventstream": ["@aws-sdk/middleware-eventstream@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-XQSH8gzLkk8CDUDxyt4Rdm9owTpRIPdtg2yw9Y2Wl5iSI55YQSiC3x8nM3c4Y4WqReJprunFPK225ZUDoYCfZA=="], + "@aws-sdk/middleware-eventstream": ["@aws-sdk/middleware-eventstream@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-VWndapHYCfwLgPpCb/xwlMKG4imhFzKJzZcKOEioGn7OHY+6gdr0K7oqy1HZgbLa3ACznZ9fku+DzmAi8fUC0g=="], - "@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-P38/v1l6HjuB2aFUewt7ueAW5IvKkFcv5dalPtbMGRhLeyivBOHwbCyuRKgVs7z7ClTpu9EaViEGki2jEQqEsQ=="], + "@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-mvWqvm61bmZUKmmrtl2uWbokqpenY3Mc3Jf4nXB/Hse6gWxLPaCQThmhPBDzsPSV8/Odn8V6ovWt3pZ7vy4BFQ=="], - "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.758.0", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/is-array-buffer": "^4.0.0", "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-o8Rk71S08YTKLoSobucjnbj97OCGaXgpEDNKXpXaavUM5xLNoHCLSUPRCiEN86Ivqxg1n17Y2nSRhfbsveOXXA=="], + "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.973.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "^3.973.18", "@aws-sdk/crc64-nvme": "^3.972.4", "@aws-sdk/types": "^3.973.5", "@smithy/is-array-buffer": "^4.2.2", "@smithy/node-config-provider": "^4.3.11", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-middleware": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-7CH2jcGmkvkHc5Buz9IGbdjq1729AAlgYJiAvGq7qhCHqYleCsriWdSnmsqWTwdAfXHMT+pkxX3w6v5tJNcSug=="], - "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw=="], + "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ=="], - "@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-EJEIXwCQhto/cBfHdm3ZOeLxd2NlJD+X2F+ZTOxzokuhBtY0IONfC/91hOo5tWQweerojwshSMHRCKzRv1tlwg=="], + "@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-vdK1LJfffBp87Lj0Bw3WdK1rJk9OLDYdQpqoKgmpIZPe+4+HawZ6THTbvjhJt4C4MNnRrHTKHQjkwBiIpDBoig=="], - "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w=="], + "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w=="], - "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA=="], + "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ=="], - "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-arn-parser": "3.723.0", "@smithy/core": "^3.1.5", "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A=="], + "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.972.18", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-arn-parser": "^3.972.3", "@smithy/core": "^3.23.8", "@smithy/node-config-provider": "^4.3.11", "@smithy/protocol-http": "^5.3.11", "@smithy/signature-v4": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-5E3XxaElrdyk6ZJ0TjH7Qm6ios4b/qQCiLr6oQ8NK7e4Kn6JBTJCaYioQCQ65BpZ1+l1mK5wTAac2+pEz0Smpw=="], - "@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-d4yd1RrPW/sspEXizq2NSOUivnheac6LPeLSLnaeTbBG9g1KqIqvCzP1TfXEqv2CrWfHEsWtJpX7oyjySSPvDQ=="], + "@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-G9clGVuAml7d8DYzY6DnRi7TIIDRvZ3YpqJPz/8wnWS5fYx/FNWNmkO6iJVlVkQg9BfeMzd+bVPtPJOvC4B+nQ=="], - "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@smithy/core": "^3.1.5", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-iNyehQXtQlj69JCgfaOssgZD4HeYGOwxcaKeG6F+40cwBjTAi0+Ph1yfDwqk2qiBPIRWJ/9l2LodZbxiBqgrwg=="], + "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.972.19", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@smithy/core": "^3.23.8", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-retry": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-Km90fcXt3W/iqujHzuM6IaDkYCj73gsYufcuWXApWdzoTy6KGk8fnchAjePMARU0xegIR3K4N3yIo1vy7OVe8A=="], - "@aws-sdk/middleware-websocket": ["@aws-sdk/middleware-websocket@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/util-format-url": "3.936.0", "@smithy/eventstream-codec": "^4.2.5", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/types": "^4.9.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-bPe3rqeugyj/MmjP0yBSZox2v1Wa8Dv39KN+RxVbQroLO8VUitBo6xyZ0oZebhZ5sASwSg58aDcMlX0uFLQnTA=="], + "@aws-sdk/middleware-websocket": ["@aws-sdk/middleware-websocket@3.972.12", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-format-url": "^3.972.7", "@smithy/eventstream-codec": "^4.2.11", "@smithy/eventstream-serde-browser": "^4.2.11", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/protocol-http": "^5.3.11", "@smithy/signature-v4": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-iyPP6FVDKe/5wy5ojC0akpDFG1vX3FeCUU47JuwN8xfvT66xlEI8qUJZPtN55TJVFzzWZJpWL78eqUE31md08Q=="], - "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.952.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OtuirjxuOqZyDcI0q4WtoyWfkq3nSnbH41JwJQsXJefduWcww1FQe5TL1JfYCU7seUxHzK8rg2nFxUBuqUlZtg=="], + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.996.7", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.18", "@aws-sdk/middleware-host-header": "^3.972.7", "@aws-sdk/middleware-logger": "^3.972.7", "@aws-sdk/middleware-recursion-detection": "^3.972.7", "@aws-sdk/middleware-user-agent": "^3.972.19", "@aws-sdk/region-config-resolver": "^3.972.7", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@aws-sdk/util-user-agent-browser": "^3.972.7", "@aws-sdk/util-user-agent-node": "^3.973.4", "@smithy/config-resolver": "^4.4.10", "@smithy/core": "^3.23.8", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/hash-node": "^4.2.11", "@smithy/invalid-dependency": "^4.2.11", "@smithy/middleware-content-length": "^4.2.11", "@smithy/middleware-endpoint": "^4.4.22", "@smithy/middleware-retry": "^4.4.39", "@smithy/middleware-serde": "^4.2.12", "@smithy/middleware-stack": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/node-http-handler": "^4.4.14", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.38", "@smithy/util-defaults-mode-node": "^4.2.41", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-MlGWA8uPaOs5AiTZ5JLM4uuWDm9EEAnm9cqwvqQIc6kEgel/8s1BaOWm9QgUcfc9K8qd7KkC3n43yDbeXOA2tg=="], - "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/node-config-provider": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ=="], + "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/config-resolver": "^4.4.10", "@smithy/node-config-provider": "^4.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA=="], "@aws-sdk/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.758.0", "", { "dependencies": { "@aws-sdk/signature-v4-multi-region": "3.758.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-format-url": "3.734.0", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dVyItwu/J1InfJBbCPpHRV9jrsBfI7L0RlDGyS3x/xqBwnm5qpvgNZQasQiyqIl+WJB4f5rZRZHgHuwftqINbA=="], - "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.758.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw=="], + "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.6", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "^3.972.18", "@aws-sdk/types": "^3.973.5", "@smithy/protocol-http": "^5.3.11", "@smithy/signature-v4": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-NnsOQsVmJXy4+IdPFUjRCWPn9qNH1TzS/f7MiWgXeoHs903tJpAWQWQtoFvLccyPoBgomKP9L89RRr2YsT/L0g=="], - "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-IpQVC9WOeXQlCEcFVNXWDIKy92CH1Az37u9K0H3DF/HT56AjhyDVKQQfHUy00nt7bHFe3u0K5+zlwErBeKy5ZA=="], + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1004.0", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/nested-clients": "^3.996.7", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-j9BwZZId9sFp+4GPhf6KrwO8Tben2sXibZA8D1vv2I1zBdvkUHcBA2g4pkqIpTRalMTLC0NPkBPX0gERxfy/iA=="], - "@aws-sdk/types": ["@aws-sdk/types@3.734.0", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg=="], + "@aws-sdk/types": ["@aws-sdk/types@3.973.5", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ=="], - "@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.723.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w=="], + "@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.972.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA=="], - "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.743.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "@smithy/util-endpoints": "^3.0.1", "tslib": "^2.6.2" } }, "sha512-sN1l559zrixeh5x+pttrnd0A3+r34r0tmPkJ/eaaMaAzXqsmKU/xYre9K3FNnsSS1J1k4PEfk/nHDTVUgFYjnw=="], + "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.996.4", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-endpoints": "^3.3.2", "tslib": "^2.6.2" } }, "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA=="], "@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-TxZMVm8V4aR/QkW9/NhujvYpPZjUYqzLwSge5imKZbWFR806NP7RMwc5ilVuHF/bMOln/cVHkl42kATElWBvNw=="], "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.568.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig=="], - "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.734.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/types": "^4.1.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng=="], + "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw=="], - "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.758.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/node-config-provider": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-A5EZw85V6WhoKMV2hbuFRvb9NPlxEErb4HPO6/SPXYY4QrjprIzScHxikqcWv1w4J3apB1wto9LPU3IMsYtfrw=="], + "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.973.4", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.19", "@aws-sdk/types": "^3.973.5", "@smithy/node-config-provider": "^4.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-uqKeLqZ9D3nQjH7HGIERNXK9qnSpUK08l4MlJ5/NZqSSdeJsVANYp437EM9sEzwU28c2xfj2V6qlkqzsgtKs6Q=="], - "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.734.0", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Zrjxi5qwGEcUsJ0ru7fRtW74WcTS0rbLcehoFB+rN1GRi2hbLcFaYs4PwVA5diLeAJH0gszv3x4Hr/S87MfbKQ=="], + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.10", "", { "dependencies": { "@smithy/types": "^4.13.0", "fast-xml-parser": "5.4.1", "tslib": "^2.6.2" } }, "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA=="], "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.2", "", {}, "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg=="], @@ -647,7 +679,9 @@ "@azure/search-documents": ["@azure/search-documents@12.0.0", "", { "dependencies": { "@azure/core-auth": "^1.3.0", "@azure/core-client": "^1.3.0", "@azure/core-http-compat": "^2.0.1", "@azure/core-paging": "^1.1.1", "@azure/core-rest-pipeline": "^1.3.0", "@azure/core-tracing": "^1.0.0", "@azure/logger": "^1.0.0", "events": "^3.0.0", "tslib": "^2.2.0" } }, "sha512-d9d53f2WWBpLHifk+LVn+AG52zuXvjgxJAdaH6kuT2qwrO1natcigtTgBM8qrI3iDYaDXsQhJSIMEgg9WKSoWA=="], - "@azure/storage-blob": ["@azure/storage-blob@12.27.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.4.0", "@azure/core-client": "^1.6.2", "@azure/core-http-compat": "^2.0.0", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.1.1", "@azure/core-rest-pipeline": "^1.10.1", "@azure/core-tracing": "^1.1.2", "@azure/core-util": "^1.6.1", "@azure/core-xml": "^1.4.3", "@azure/logger": "^1.0.0", "events": "^3.0.0", "tslib": "^2.2.0" } }, "sha512-IQjj9RIzAKatmNca3D6bT0qJ+Pkox1WZGOg2esJF2YLHb45pQKOwGPIAV+w3rfgkj7zV3RMxpn/c6iftzSOZJQ=="], + "@azure/storage-blob": ["@azure/storage-blob@12.31.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.3", "@azure/core-http-compat": "^2.2.0", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.6.2", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/core-xml": "^1.4.5", "@azure/logger": "^1.1.4", "@azure/storage-common": "^12.3.0", "events": "^3.0.0", "tslib": "^2.8.1" } }, "sha512-DBgNv10aCSxopt92DkTDD0o9xScXeBqPKGmR50FPZQaEcH4JLQ+GEOGEDv19V5BMkB7kxr+m4h6il/cCDPvmHg=="], + + "@azure/storage-common": ["@azure/storage-common@12.3.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-http-compat": "^2.2.0", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.1.4", "events": "^3.3.0", "tslib": "^2.8.1" } }, "sha512-/OFHhy86aG5Pe8dP5tsp+BuJ25JOAl9yaMU3WZbkeoiFMHFtJ7tu5ili7qEdBXNW9G5lDB19trwyI6V49F/8iQ=="], "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], @@ -657,45 +691,33 @@ "@babel/generator": ["@babel/generator@7.28.5", "", { "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" } }, "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ=="], - "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g=="], - - "@babel/helper-builder-binary-assignment-operator-visitor": ["@babel/helper-builder-binary-assignment-operator-visitor@7.22.15", "", { "dependencies": { "@babel/types": "^7.22.15" } }, "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw=="], + "@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "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" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="], - "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.26.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/helper-replace-supers": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/traverse": "^7.26.9", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg=="], + "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], "@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.26.3", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong=="], - "@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.3", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg=="], - - "@babel/helper-environment-visitor": ["@babel/helper-environment-visitor@7.22.20", "", {}, "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA=="], - - "@babel/helper-function-name": ["@babel/helper-function-name@7.23.0", "", { "dependencies": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" } }, "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw=="], + "@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="], - "@babel/helper-hoist-variables": ["@babel/helper-hoist-variables@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw=="], - - "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ=="], + "@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], "@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="], "@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw=="], - "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ=="], + "@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], "@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="], - "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw=="], + "@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="], - "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.26.5", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", "@babel/traverse": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg=="], + "@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - "@babel/helper-simple-access": ["@babel/helper-simple-access@7.24.7", "", { "dependencies": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" } }, "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg=="], - - "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.25.9", "", { "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA=="], - - "@babel/helper-split-export-declaration": ["@babel/helper-split-export-declaration@7.22.6", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g=="], + "@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], @@ -703,21 +725,21 @@ "@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="], - "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.25.9", "", { "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" } }, "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g=="], + "@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="], "@babel/helpers": ["@babel/helpers@7.28.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" } }, "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w=="], "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" } }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g=="], + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q=="], - "@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw=="], + "@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA=="], - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug=="], + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA=="], - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", "@babel/plugin-transform-optional-chaining": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g=="], + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw=="], - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg=="], + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw=="], "@babel/plugin-proposal-private-property-in-object": ["@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2", "", { "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w=="], @@ -729,11 +751,7 @@ "@babel/plugin-syntax-class-static-block": ["@babel/plugin-syntax-class-static-block@7.14.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw=="], - "@babel/plugin-syntax-dynamic-import": ["@babel/plugin-syntax-dynamic-import@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ=="], - - "@babel/plugin-syntax-export-namespace-from": ["@babel/plugin-syntax-export-namespace-from@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q=="], - - "@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.26.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg=="], + "@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg=="], "@babel/plugin-syntax-import-attributes": ["@babel/plugin-syntax-import-attributes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww=="], @@ -763,131 +781,131 @@ "@babel/plugin-syntax-unicode-sets-regex": ["@babel/plugin-syntax-unicode-sets-regex@7.18.6", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg=="], - "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg=="], + "@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="], - "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.26.8", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-remap-async-to-generator": "^7.25.9", "@babel/traverse": "^7.26.8" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg=="], + "@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1", "@babel/traverse": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q=="], - "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.25.9", "", { "dependencies": { "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-remap-async-to-generator": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ=="], + "@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA=="], - "@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.26.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ=="], + "@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg=="], - "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg=="], + "@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g=="], - "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q=="], + "@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA=="], - "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.26.0", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ=="], + "@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg=="], - "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9", "@babel/traverse": "^7.25.9", "globals": "^11.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg=="], + "@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA=="], - "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA=="], + "@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/template": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw=="], - "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ=="], + "@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="], - "@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA=="], + "@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw=="], - "@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw=="], + "@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q=="], - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog=="], + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ=="], - "@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg=="], + "@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A=="], "@babel/plugin-transform-explicit-resource-management": ["@babel/plugin-transform-explicit-resource-management@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ=="], - "@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.26.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ=="], + "@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw=="], - "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww=="], + "@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="], - "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.26.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg=="], + "@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="], - "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.25.9", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA=="], + "@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="], - "@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw=="], + "@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q=="], - "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ=="], + "@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="], - "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q=="], + "@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA=="], - "@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA=="], + "@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ=="], - "@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw=="], + "@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA=="], - "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.26.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.26.0", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ=="], + "@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="], - "@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9", "@babel/traverse": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA=="], + "@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.28.5", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew=="], - "@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.25.9", "", { "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw=="], + "@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w=="], - "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA=="], + "@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng=="], - "@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ=="], + "@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ=="], - "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.26.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw=="], + "@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA=="], - "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q=="], + "@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw=="], - "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.25.9", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", "@babel/plugin-transform-parameters": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg=="], + "@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.4", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew=="], - "@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A=="], + "@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng=="], - "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g=="], + "@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q=="], - "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A=="], + "@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ=="], - "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g=="], + "@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="], - "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.25.9", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw=="], + "@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA=="], - "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.25.9", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw=="], + "@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ=="], - "@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA=="], + "@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ=="], - "@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw=="], + "@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA=="], - "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.23.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-jsx": "^7.23.3", "@babel/types": "^7.23.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA=="], + "@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/types": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw=="], - "@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.22.5", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A=="], + "@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="], - "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg=="], + "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], - "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg=="], + "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], - "@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.23.3", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ=="], + "@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA=="], - "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg=="], + "@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA=="], - "@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.26.0", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw=="], + "@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA=="], - "@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg=="], + "@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw=="], "@babel/plugin-transform-runtime": ["@babel/plugin-transform-runtime@7.23.9", "", { "dependencies": { "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "babel-plugin-polyfill-corejs2": "^0.4.8", "babel-plugin-polyfill-corejs3": "^0.9.0", "babel-plugin-polyfill-regenerator": "^0.5.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ=="], - "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng=="], + "@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="], - "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A=="], + "@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q=="], - "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA=="], + "@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="], - "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.26.8", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q=="], + "@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="], - "@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.26.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.26.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw=="], + "@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw=="], - "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.23.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-create-class-features-plugin": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-typescript": "^7.23.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA=="], + "@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA=="], - "@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.25.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q=="], + "@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg=="], - "@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg=="], + "@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q=="], - "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA=="], + "@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="], - "@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.25.9", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ=="], + "@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw=="], - "@babel/preset-env": ["@babel/preset-env@7.26.9", "", { "dependencies": { "@babel/compat-data": "^7.26.8", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.26.0", "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.25.9", "@babel/plugin-transform-async-generator-functions": "^7.26.8", "@babel/plugin-transform-async-to-generator": "^7.25.9", "@babel/plugin-transform-block-scoped-functions": "^7.26.5", "@babel/plugin-transform-block-scoping": "^7.25.9", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-class-static-block": "^7.26.0", "@babel/plugin-transform-classes": "^7.25.9", "@babel/plugin-transform-computed-properties": "^7.25.9", "@babel/plugin-transform-destructuring": "^7.25.9", "@babel/plugin-transform-dotall-regex": "^7.25.9", "@babel/plugin-transform-duplicate-keys": "^7.25.9", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-dynamic-import": "^7.25.9", "@babel/plugin-transform-exponentiation-operator": "^7.26.3", "@babel/plugin-transform-export-namespace-from": "^7.25.9", "@babel/plugin-transform-for-of": "^7.26.9", "@babel/plugin-transform-function-name": "^7.25.9", "@babel/plugin-transform-json-strings": "^7.25.9", "@babel/plugin-transform-literals": "^7.25.9", "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", "@babel/plugin-transform-member-expression-literals": "^7.25.9", "@babel/plugin-transform-modules-amd": "^7.25.9", "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/plugin-transform-modules-systemjs": "^7.25.9", "@babel/plugin-transform-modules-umd": "^7.25.9", "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", "@babel/plugin-transform-new-target": "^7.25.9", "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-rest-spread": "^7.25.9", "@babel/plugin-transform-object-super": "^7.25.9", "@babel/plugin-transform-optional-catch-binding": "^7.25.9", "@babel/plugin-transform-optional-chaining": "^7.25.9", "@babel/plugin-transform-parameters": "^7.25.9", "@babel/plugin-transform-private-methods": "^7.25.9", "@babel/plugin-transform-private-property-in-object": "^7.25.9", "@babel/plugin-transform-property-literals": "^7.25.9", "@babel/plugin-transform-regenerator": "^7.25.9", "@babel/plugin-transform-regexp-modifiers": "^7.26.0", "@babel/plugin-transform-reserved-words": "^7.25.9", "@babel/plugin-transform-shorthand-properties": "^7.25.9", "@babel/plugin-transform-spread": "^7.25.9", "@babel/plugin-transform-sticky-regex": "^7.25.9", "@babel/plugin-transform-template-literals": "^7.26.8", "@babel/plugin-transform-typeof-symbol": "^7.26.7", "@babel/plugin-transform-unicode-escapes": "^7.25.9", "@babel/plugin-transform-unicode-property-regex": "^7.25.9", "@babel/plugin-transform-unicode-regex": "^7.25.9", "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ=="], + "@babel/preset-env": ["@babel/preset-env@7.28.5", "", { "dependencies": { "@babel/compat-data": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", "@babel/plugin-transform-block-scoping": "^7.28.5", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", "@babel/plugin-transform-classes": "^7.28.4", "@babel/plugin-transform-computed-properties": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", "@babel/plugin-transform-exponentiation-operator": "^7.28.5", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", "@babel/plugin-transform-object-rest-spread": "^7.28.4", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", "@babel/plugin-transform-regenerator": "^7.28.4", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-spread": "^7.27.1", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", "@babel/plugin-transform-unicode-property-regex": "^7.27.1", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg=="], "@babel/preset-modules": ["@babel/preset-modules@0.1.6-no-external-plugins", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA=="], - "@babel/preset-react": ["@babel/preset-react@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", "@babel/plugin-transform-react-display-name": "^7.23.3", "@babel/plugin-transform-react-jsx": "^7.22.15", "@babel/plugin-transform-react-jsx-development": "^7.22.5", "@babel/plugin-transform-react-pure-annotations": "^7.23.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w=="], + "@babel/preset-react": ["@babel/preset-react@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ=="], - "@babel/preset-typescript": ["@babel/preset-typescript@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.22.15", "@babel/plugin-syntax-jsx": "^7.23.3", "@babel/plugin-transform-modules-commonjs": "^7.23.3", "@babel/plugin-transform-typescript": "^7.23.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ=="], + "@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="], "@babel/runtime": ["@babel/runtime@7.26.10", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw=="], @@ -899,8 +917,22 @@ "@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="], + "@borewit/text-codec": ["@borewit/text-codec@0.2.2", "", {}, "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ=="], + + "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="], + "@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="], + "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@11.1.2", "", { "dependencies": { "@chevrotain/gast": "11.1.2", "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q=="], + + "@chevrotain/gast": ["@chevrotain/gast@11.1.2", "", { "dependencies": { "@chevrotain/types": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g=="], + + "@chevrotain/regexp-to-ast": ["@chevrotain/regexp-to-ast@11.1.2", "", {}, "sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw=="], + + "@chevrotain/types": ["@chevrotain/types@11.1.2", "", {}, "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw=="], + + "@chevrotain/utils": ["@chevrotain/utils@11.1.2", "", {}, "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA=="], + "@codemirror/autocomplete": ["@codemirror/autocomplete@6.18.0", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" }, "peerDependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", "@lezer/common": "^1.0.0" } }, "sha512-5DbOvBbY4qW5l57cjDsmmpDh3/TeK1vXfTHa+BUMrRzdWdcxKZ4U4V7vQaTtOpApNU4kLS4FQ6cINtLg245LXA=="], "@codemirror/commands": ["@codemirror/commands@6.6.0", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg=="], @@ -929,67 +961,109 @@ "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], - "@csstools/cascade-layer-name-parser": ["@csstools/cascade-layer-name-parser@1.0.7", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3" } }, "sha512-9J4aMRJ7A2WRjaRLvsMeWrL69FmEuijtiW1XlK/sG+V0UJiHVYUyvj9mY4WAXfU/hGIiGOgL8e0jJcRyaZTjDQ=="], + "@csstools/cascade-layer-name-parser": ["@csstools/cascade-layer-name-parser@3.0.0", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-/3iksyevwRfSJx5yH0RkcrcYXwuhMQx3Juqf40t97PeEy2/Mz2TItZ/z/216qpe4GgOyFBP8MKIwVvytzHmfIQ=="], - "@csstools/color-helpers": ["@csstools/color-helpers@2.1.0", "", {}, "sha512-OWkqBa7PDzZuJ3Ha7T5bxdSVfSCfTq6K1mbAhbO1MD+GSULGjrp45i5RudyJOedstSarN/3mdwu9upJE7gDXfw=="], + "@csstools/color-helpers": ["@csstools/color-helpers@6.0.2", "", {}, "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q=="], - "@csstools/css-calc": ["@csstools/css-calc@1.1.6", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3" } }, "sha512-YHPAuFg5iA4qZGzMzvrQwzkvJpesXXyIUyaONflQrjtHB+BcFFbgltJkIkb31dMGO4SE9iZFA4HYpdk7+hnYew=="], + "@csstools/css-calc": ["@csstools/css-calc@3.1.1", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ=="], - "@csstools/css-color-parser": ["@csstools/css-color-parser@1.5.1", "", { "dependencies": { "@csstools/color-helpers": "^4.0.0", "@csstools/css-calc": "^1.1.6" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3" } }, "sha512-x+SajGB2paGrTjPOUorGi8iCztF008YMKXTn+XzGVDBEIVJ/W1121pPerpneJYGOe1m6zWLPLnzOPaznmQxKFw=="], + "@csstools/css-color-parser": ["@csstools/css-color-parser@4.0.2", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "@csstools/css-calc": "^3.1.1" }, "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw=="], - "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@2.5.0", "", { "peerDependencies": { "@csstools/css-tokenizer": "^2.2.3" } }, "sha512-abypo6m9re3clXA00eu5syw+oaPHbJTPapu9C4pzNsJ4hdZDzushT50Zhu+iIYXgEe1CxnRMn7ngsbV+MLrlpQ=="], + "@csstools/css-parser-algorithms": ["@csstools/css-parser-algorithms@4.0.0", "", { "peerDependencies": { "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w=="], - "@csstools/css-tokenizer": ["@csstools/css-tokenizer@2.2.3", "", {}, "sha512-pp//EvZ9dUmGuGtG1p+n17gTHEOqu9jO+FiCUjNN3BDmyhdA2Jq9QsVeR7K8/2QCK17HSsioPlTW9ZkzoWb3Lg=="], + "@csstools/css-tokenizer": ["@csstools/css-tokenizer@4.0.0", "", {}, "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA=="], - "@csstools/media-query-list-parser": ["@csstools/media-query-list-parser@2.1.7", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3" } }, "sha512-lHPKJDkPUECsyAvD60joYfDmp8UERYxHGkFfyLJFTVK/ERJe0sVlIFLXU5XFxdjNDTerp5L4KeaKG+Z5S94qxQ=="], + "@csstools/media-query-list-parser": ["@csstools/media-query-list-parser@5.0.0", "", { "peerDependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-T9lXmZOfnam3eMERPsszjY5NK0jX8RmThmmm99FZ8b7z8yMaFZWKwLWGZuTwdO3ddRY5fy13GmmEYZXB4I98Eg=="], - "@csstools/postcss-cascade-layers": ["@csstools/postcss-cascade-layers@3.0.1", "", { "dependencies": { "@csstools/selector-specificity": "^2.0.2", "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-dD8W98dOYNOH/yX4V4HXOhfCOnvVAg8TtsL+qCGNoKXuq5z2C/d026wGWgySgC8cajXXo/wNezS31Glj5GcqrA=="], + "@csstools/postcss-alpha-function": ["@csstools/postcss-alpha-function@2.0.3", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-8GqzD3JnfpKJSVxPIC0KadyAfB5VRzPZdv7XQ4zvK1q0ku+uHVUAS2N/IDavQkW40gkuUci64O0ea6QB/zgCSw=="], - "@csstools/postcss-color-function": ["@csstools/postcss-color-function@2.2.3", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-b1ptNkr1UWP96EEHqKBWWaV5m/0hgYGctgA/RVZhONeP1L3T/8hwoqDm9bB23yVCfOgE9U93KI9j06+pEkJTvw=="], + "@csstools/postcss-cascade-layers": ["@csstools/postcss-cascade-layers@6.0.0", "", { "dependencies": { "@csstools/selector-specificity": "^6.0.0", "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-WhsECqmrEZQGqaPlBA7JkmF/CJ2/+wetL4fkL9sOPccKd32PQ1qToFM6gqSI5rkpmYqubvbxjEJhyMTHYK0vZQ=="], - "@csstools/postcss-color-mix-function": ["@csstools/postcss-color-mix-function@1.0.3", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-QGXjGugTluqFZWzVf+S3wCiRiI0ukXlYqCi7OnpDotP/zaVTyl/aqZujLFzTOXy24BoWnu89frGMc79ohY5eog=="], + "@csstools/postcss-color-function": ["@csstools/postcss-color-function@5.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-CjBdFemUFcAh3087MEJhZcO+QT1b8S75agysa1rU9TEC1YecznzwV+jpMxUc0JRBEV4ET2PjLssqmndR9IygeA=="], - "@csstools/postcss-font-format-keywords": ["@csstools/postcss-font-format-keywords@2.0.2", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-iKYZlIs6JsNT7NKyRjyIyezTCHLh4L4BBB3F5Nx7Dc4Z/QmBgX+YJFuUSar8IM6KclGiAUFGomXFdYxAwJydlA=="], + "@csstools/postcss-color-function-display-p3-linear": ["@csstools/postcss-color-function-display-p3-linear@2.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-TWUwSe1+2KdYGGWTx5LR4JQN07vKHAeSho+bGYRgow+9cs3dqgOqS1f/a1odiX30ESmZvwIudJ86wzeiDR6UGg=="], - "@csstools/postcss-gradients-interpolation-method": ["@csstools/postcss-gradients-interpolation-method@3.0.6", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-rBOBTat/YMmB0G8VHwKqDEx+RZ4KCU9j42K8LwS0IpZnyThalZZF7BCSsZ6TFlZhcRZKlZy3LLFI2pLqjNVGGA=="], + "@csstools/postcss-color-mix-function": ["@csstools/postcss-color-mix-function@4.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-PFKQKswFqZrYKpajZsP4lhqjU/6+J5PTOWq1rKiFnniKsf4LgpGXrgHS/C6nn5Rc51LX0n4dWOWqY5ZN2i5IjA=="], - "@csstools/postcss-hwb-function": ["@csstools/postcss-hwb-function@2.2.2", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-W5Y5oaJ382HSlbdGfPf60d7dAK6Hqf10+Be1yZbd/TNNrQ/3dDdV1c07YwOXPQ3PZ6dvFMhxbIbn8EC3ki3nEg=="], + "@csstools/postcss-color-mix-variadic-function-arguments": ["@csstools/postcss-color-mix-variadic-function-arguments@2.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-zEchsghpDH/6SytyjKu9TIPm4hiiWcur102cENl54cyIwTZsa+2MBJl/vtyALZ+uQ17h27L4waD+0Ow96sgZow=="], - "@csstools/postcss-ic-unit": ["@csstools/postcss-ic-unit@2.0.4", "", { "dependencies": { "@csstools/postcss-progressive-custom-properties": "^2.3.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-9W2ZbV7whWnr1Gt4qYgxMWzbevZMOvclUczT5vk4yR6vS53W/njiiUhtm/jh/BKYwQ1W3PECZjgAd2dH4ebJig=="], + "@csstools/postcss-content-alt-text": ["@csstools/postcss-content-alt-text@3.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-OHa+4aCcrJtHpPWB3zptScHwpS1TUbeLR4uO0ntIz0Su/zw9SoWkVu+tDMSySSAsNtNSI3kut4fTliFwIsrHxA=="], - "@csstools/postcss-is-pseudo-class": ["@csstools/postcss-is-pseudo-class@3.2.1", "", { "dependencies": { "@csstools/selector-specificity": "^2.0.0", "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-AtANdV34kJl04Al62is3eQRk/BfOfyAvEmRJvbt+nx5REqImLC+2XhuE6skgkcPli1l8ONS67wS+l1sBzySc3Q=="], + "@csstools/postcss-contrast-color-function": ["@csstools/postcss-contrast-color-function@3.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-fwOz/m+ytFPz4aIph2foQS9nEDOdOjYcN5bgwbGR2jGUV8mYaeD/EaTVMHTRb/zqB65y2qNwmcFcE6VQty69Pw=="], - "@csstools/postcss-logical-float-and-clear": ["@csstools/postcss-logical-float-and-clear@1.0.1", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-eO9z2sMLddvlfFEW5Fxbjyd03zaO7cJafDurK4rCqyRt9P7aaWwha0LcSzoROlcZrw1NBV2JAp2vMKfPMQO1xw=="], + "@csstools/postcss-exponential-functions": ["@csstools/postcss-exponential-functions@3.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-WHJ52Uk0AVUIICEYRY9xFHJZAuq0ZVg0f8xzqUN2zRFrZvGgRPpFwxK7h9FWvqKIOueOwN6hnJD23A8FwsUiVw=="], - "@csstools/postcss-logical-resize": ["@csstools/postcss-logical-resize@1.0.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-x1ge74eCSvpBkDDWppl+7FuD2dL68WP+wwP2qvdUcKY17vJksz+XoE1ZRV38uJgS6FNUwC0AxrPW5gy3MxsDHQ=="], + "@csstools/postcss-font-format-keywords": ["@csstools/postcss-font-format-keywords@5.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-M1EjCe/J3u8fFhOZgRci74cQhJ7R0UFBX6T+WqoEvjrr8hVfMiV+HTYrzxLY5OW8YllvXYr5Q5t5OvJbsUSeDg=="], - "@csstools/postcss-logical-viewport-units": ["@csstools/postcss-logical-viewport-units@1.0.3", "", { "dependencies": { "@csstools/css-tokenizer": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-6zqcyRg9HSqIHIPMYdt6THWhRmE5/tyHKJQLysn2TeDf/ftq7Em9qwMTx98t2C/7UxIsYS8lOiHHxAVjWn2WUg=="], + "@csstools/postcss-font-width-property": ["@csstools/postcss-font-width-property@1.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-AvmySApdijbjYQuXXh95tb7iVnqZBbJrv3oajO927ksE/mDmJBiszm+psW8orL2lRGR8j6ZU5Uv9/ou2Z5KRKA=="], - "@csstools/postcss-media-minmax": ["@csstools/postcss-media-minmax@1.1.2", "", { "dependencies": { "@csstools/css-calc": "^1.1.6", "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3", "@csstools/media-query-list-parser": "^2.1.7" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-7qTRTJxW96u2yiEaTep1+8nto1O/rEDacewKqH+Riq5E6EsHTOmGHxkB4Se5Ic5xgDC4I05lLZxzzxnlnSypxA=="], + "@csstools/postcss-gamut-mapping": ["@csstools/postcss-gamut-mapping@3.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-IrXAW3KQ3Sxm29C3/4mYQ/iA0Q5OH9YFOPQ2w24iIlXpD06A9MHvmQapP2vAGtQI3tlp2Xw5LIdm9F8khARfOA=="], - "@csstools/postcss-media-queries-aspect-ratio-number-values": ["@csstools/postcss-media-queries-aspect-ratio-number-values@1.0.4", "", { "dependencies": { "@csstools/css-parser-algorithms": "^2.2.0", "@csstools/css-tokenizer": "^2.1.1", "@csstools/media-query-list-parser": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-IwyTbyR8E2y3kh6Fhrs251KjKBJeUPV5GlnUKnpU70PRFEN2DolWbf2V4+o/B9+Oj77P/DullLTulWEQ8uFtAA=="], + "@csstools/postcss-gradients-interpolation-method": ["@csstools/postcss-gradients-interpolation-method@6.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-saQHvD1PD/zCdn+kxCWCcQOdXZBljr8L6BKlCLs0w8GXYfo3SHdWL1HZQ+I1hVCPlU+MJPJJbZJjG/jHRJSlAw=="], - "@csstools/postcss-nested-calc": ["@csstools/postcss-nested-calc@2.0.2", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-jbwrP8rN4e7LNaRcpx3xpMUjhtt34I9OV+zgbcsYAAk6k1+3kODXJBf95/JMYWhu9g1oif7r06QVUgfWsKxCFw=="], + "@csstools/postcss-hwb-function": ["@csstools/postcss-hwb-function@5.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-ChR0+pKc/2cs900jakiv8dLrb69aez5P3T+g+wfJx1j6mreAe8orKTiMrVBk+DZvCRqpdOA2m8VoFms64A3Dew=="], - "@csstools/postcss-normalize-display-values": ["@csstools/postcss-normalize-display-values@2.0.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-TQT5g3JQ5gPXC239YuRK8jFceXF9d25ZvBkyjzBGGoW5st5sPXFVQS8OjYb9IJ/K3CdfK4528y483cgS2DJR/w=="], + "@csstools/postcss-ic-unit": ["@csstools/postcss-ic-unit@5.0.0", "", { "dependencies": { "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-/ws5d6c4uKqfM9zIL3ugcGI+3fvZEOOkJHNzAyTAGJIdZ+aSL9BVPNlHGV4QzmL0vqBSCOdU3+rhcMEj3+KzYw=="], - "@csstools/postcss-oklab-function": ["@csstools/postcss-oklab-function@2.2.3", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-AgJ2rWMnLCDcbSMTHSqBYn66DNLBym6JpBpCaqmwZ9huGdljjDRuH3DzOYzkgQ7Pm2K92IYIq54IvFHloUOdvA=="], + "@csstools/postcss-initial": ["@csstools/postcss-initial@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-UVUrFmrTQyLomVepnjWlbBg7GoscLmXLwYFyjbcEnmpeGW7wde6lNpx5eM3eVwZI2M+7hCE3ykYnAsEPLcLa+Q=="], - "@csstools/postcss-progressive-custom-properties": ["@csstools/postcss-progressive-custom-properties@2.3.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Zd8ojyMlsL919TBExQ1I0CTpBDdyCpH/yOdqatZpuC3sd22K4SwC7+Yez3Q/vmXMWSAl+shjNeFZ7JMyxMjK+Q=="], + "@csstools/postcss-is-pseudo-class": ["@csstools/postcss-is-pseudo-class@6.0.0", "", { "dependencies": { "@csstools/selector-specificity": "^6.0.0", "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-1Hdy/ykg9RDo8vU8RiM2o+RaXO39WpFPaIkHxlAEJFofle/lc33tdQMKhBk3jR/Fe+uZNLOs3HlowFafyFptVw=="], - "@csstools/postcss-relative-color-syntax": ["@csstools/postcss-relative-color-syntax@1.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-juCoVInkgH2TZPfOhyx6tIal7jW37L/0Tt+Vcl1LoxqQA9sxcg3JWYZ98pl1BonDnki6s/M7nXzFQHWsWMeHgw=="], + "@csstools/postcss-light-dark-function": ["@csstools/postcss-light-dark-function@3.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-s++V5/hYazeRUCYIn2lsBVzUsxdeC46gtwpgW6lu5U/GlPOS5UTDT14kkEyPgXmFbCvaWLREqV7YTMJq1K3G6w=="], - "@csstools/postcss-scope-pseudo-class": ["@csstools/postcss-scope-pseudo-class@2.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-6Pvo4uexUCXt+Hz5iUtemQAcIuCYnL+ePs1khFR6/xPgC92aQLJ0zGHonWoewiBE+I++4gXK3pr+R1rlOFHe5w=="], + "@csstools/postcss-logical-float-and-clear": ["@csstools/postcss-logical-float-and-clear@4.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-NGzdIRVj/VxOa/TjVdkHeyiJoDihONV0+uB0csUdgWbFFr8xndtfqK8iIGP9IKJzco+w0hvBF2SSk2sDSTAnOQ=="], - "@csstools/postcss-stepped-value-functions": ["@csstools/postcss-stepped-value-functions@2.1.1", "", { "dependencies": { "@csstools/css-calc": "^1.1.1", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-YCvdF0GCZK35nhLgs7ippcxDlRVe5QsSht3+EghqTjnYnyl3BbWIN6fYQ1dKWYTJ+7Bgi41TgqQFfJDcp9Xy/w=="], + "@csstools/postcss-logical-overflow": ["@csstools/postcss-logical-overflow@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-5cRg93QXVskM0MNepHpPcL0WLSf5Hncky0DrFDQY/4ozbH5lH7SX5ejayVpNTGSX7IpOvu7ykQDLOdMMGYzwpA=="], - "@csstools/postcss-text-decoration-shorthand": ["@csstools/postcss-text-decoration-shorthand@2.2.4", "", { "dependencies": { "@csstools/color-helpers": "^2.1.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-zPN56sQkS/7YTCVZhOBVCWf7AiNge8fXDl7JVaHLz2RyT4pnyK2gFjckWRLpO0A2xkm1lCgZ0bepYZTwAVd/5A=="], + "@csstools/postcss-logical-overscroll-behavior": ["@csstools/postcss-logical-overscroll-behavior@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-82Jnl/5Wi5jb19nQE1XlBHrZcNL3PzOgcj268cDkfwf+xi10HBqufGo1Unwf5n8bbbEFhEKgyQW+vFsc9iY1jw=="], - "@csstools/postcss-trigonometric-functions": ["@csstools/postcss-trigonometric-functions@2.1.1", "", { "dependencies": { "@csstools/css-calc": "^1.1.1", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-XcXmHEFfHXhvYz40FtDlA4Fp4NQln2bWTsCwthd2c+MCnYArUYU3YaMqzR5CrKP3pMoGYTBnp5fMqf1HxItNyw=="], + "@csstools/postcss-logical-resize": ["@csstools/postcss-logical-resize@4.0.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-L0T3q0gei/tGetCGZU0c7VN77VTivRpz1YZRNxjXYmW+85PKeI6U9YnSvDqLU2vBT2uN4kLEzfgZ0ThIZpN18A=="], - "@csstools/postcss-unset-value": ["@csstools/postcss-unset-value@2.0.1", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-oJ9Xl29/yU8U7/pnMJRqAZd4YXNCfGEdcP4ywREuqm/xMqcgDNDppYRoCGDt40aaZQIEKBS79LytUDN/DHf0Ew=="], + "@csstools/postcss-logical-viewport-units": ["@csstools/postcss-logical-viewport-units@4.0.0", "", { "dependencies": { "@csstools/css-tokenizer": "^4.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-TA3AqVN/1IH3dKRC2UUWvprvwyOs2IeD7FDZk5Hz20w4q33yIuSg0i0gjyTUkcn90g8A4n7QpyZ2AgBrnYPnnA=="], - "@csstools/selector-specificity": ["@csstools/selector-specificity@2.2.0", "", { "peerDependencies": { "postcss-selector-parser": "^6.0.10" } }, "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw=="], + "@csstools/postcss-media-minmax": ["@csstools/postcss-media-minmax@3.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-I+CrmZt23fyejMItpLQFOg9gPXkDBBDjTqRT0UxCTZlYZfGrzZn4z+2kbXLRwDfR59OK8zaf26M4kwYwG0e1MA=="], + + "@csstools/postcss-media-queries-aspect-ratio-number-values": ["@csstools/postcss-media-queries-aspect-ratio-number-values@4.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-FDdC3lbrj8Vr0SkGIcSLTcRB7ApG6nlJFxOxkEF2C5hIZC1jtgjISFSGn/WjFdVkn8Dqe+Vx9QXI3axS2w1XHw=="], + + "@csstools/postcss-mixins": ["@csstools/postcss-mixins@1.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-rz6qjT2w9L3k65jGc2dX+3oGiSrYQ70EZPDrINSmSVoVys7lLBFH0tvEa8DW2sr9cbRVD/W+1sy8+7bfu0JUfg=="], + + "@csstools/postcss-nested-calc": ["@csstools/postcss-nested-calc@5.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-aPSw8P60e/i9BEfugauhikBqgjiwXcw3I9o4vXs+hktl4NSTgZRI0QHimxk9mst8N01A2TKDBxOln3mssRxiHQ=="], + + "@csstools/postcss-normalize-display-values": ["@csstools/postcss-normalize-display-values@5.0.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-FcbEmoxDEGYvm2W3rQzVzcuo66+dDJjzzVDs+QwRmZLHYofGmMGwIKPqzF86/YW+euMDa7sh1xjWDvz/fzByZQ=="], + + "@csstools/postcss-oklab-function": ["@csstools/postcss-oklab-function@5.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-3d/Wcnp2uW6Io0Tajl0croeUo46gwOVQI9N32PjA/HVQo6z1iL7yp19Gp+6e5E5CDKGpW7U822MsDVo2XK1z0Q=="], + + "@csstools/postcss-position-area-property": ["@csstools/postcss-position-area-property@2.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-TeEfzsJGB23Syv7yCm8AHCD2XTFujdjr9YYu9ebH64vnfCEvY4BG319jXAYSlNlf3Yc9PNJ6WnkDkUF5XVgSKQ=="], + + "@csstools/postcss-progressive-custom-properties": ["@csstools/postcss-progressive-custom-properties@5.0.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-NsJoZ89rxmDrUsITf8QIk5w+lQZQ8Xw5K6cLFG+cfiffsLYHb3zcbOOrHLetGl1WIhjWWQ4Cr8MMrg46Q+oACg=="], + + "@csstools/postcss-property-rule-prelude-list": ["@csstools/postcss-property-rule-prelude-list@2.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-qcMAkc9AhpzHgmQCD8hoJgGYifcOAxd1exXjjxilMM6euwRE619xDa4UsKBCv/v4g+sS63sd6c29LPM8s2ylSQ=="], + + "@csstools/postcss-random-function": ["@csstools/postcss-random-function@3.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-SvKGfmj+WHfn4bWHaBYlkXDyU3SlA3fL8aaYZ8Op6M8tunNf3iV9uZyZZGWMCbDw0sGeoTmYZW9nmKN8Qi/ctg=="], + + "@csstools/postcss-relative-color-syntax": ["@csstools/postcss-relative-color-syntax@4.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-HaMN+qMURinllszbps2AhXKaLeibg/2VW6FriYDrqE58ji82+z2S3/eLloywVOY8BQCJ9lZMdy6TcRQNbn9u3w=="], + + "@csstools/postcss-scope-pseudo-class": ["@csstools/postcss-scope-pseudo-class@5.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-kBrBFJcAji3MSHS4qQIihPvJfJC5xCabXLbejqDMiQi+86HD4eMBiTayAo46Urg7tlEmZZQFymFiJt+GH6nvXw=="], + + "@csstools/postcss-sign-functions": ["@csstools/postcss-sign-functions@2.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-C3br0qcHJkQ0qSGUBnDJHXQdO8XObnCpGwai5m1L2tv2nCjt0vRHG6A9aVCQHvh08OqHNM2ty1dYDNNXV99YAQ=="], + + "@csstools/postcss-stepped-value-functions": ["@csstools/postcss-stepped-value-functions@5.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-vZf7zPzRb7xIi2o5Z9q6wyeEAjoRCg74O2QvYxmQgxYO5V5cdBv4phgJDyOAOP3JHy4abQlm2YaEUS3gtGQo0g=="], + + "@csstools/postcss-syntax-descriptor-syntax-production": ["@csstools/postcss-syntax-descriptor-syntax-production@2.0.0", "", { "dependencies": { "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-elYcbdiBXAkPqvojB9kIBRuHY6htUhjSITtFQ+XiXnt6SvZCbNGxQmaaw6uZ7SPHu/+i/XVjzIt09/1k3SIerQ=="], + + "@csstools/postcss-system-ui-font-family": ["@csstools/postcss-system-ui-font-family@2.0.0", "", { "dependencies": { "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-FyGZCgchFImFyiHS2x3rD5trAqatf/x23veBLTIgbaqyFfna6RNBD+Qf8HRSjt6HGMXOLhAjxJ3OoZg0bbn7Qw=="], + + "@csstools/postcss-text-decoration-shorthand": ["@csstools/postcss-text-decoration-shorthand@5.0.3", "", { "dependencies": { "@csstools/color-helpers": "^6.0.2", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-62fjggvIM1YYfDJPcErMUDkEZB6CByG8neTJqexnZe1hRBgCjD4dnXDLoCSSurjs1LzjBq6irFDpDaOvDZfrlw=="], + + "@csstools/postcss-trigonometric-functions": ["@csstools/postcss-trigonometric-functions@5.0.1", "", { "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-e8me32Mhl8JeBnxVJgsQUYpV4Md4KiyvpILpQlaY/eK1Gwdb04kasiTTswPQ5q7Z8+FppJZ2Z4d8HRfn6rjD3w=="], + + "@csstools/postcss-unset-value": ["@csstools/postcss-unset-value@5.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-EoO54sS2KCIfesvHyFYAW99RtzwHdgaJzhl7cqKZSaMYKZv3fXSOehDjAQx8WZBKn1JrMd7xJJI1T1BxPF7/jA=="], + + "@csstools/selector-resolve-nested": ["@csstools/selector-resolve-nested@4.0.0", "", { "peerDependencies": { "postcss-selector-parser": "^7.1.1" } }, "sha512-9vAPxmp+Dx3wQBIUwc1v7Mdisw1kbbaGqXUM8QLTgWg7SoPGYtXBsMXvsFs/0Bn5yoFhcktzxNZGNaUt0VjgjA=="], + + "@csstools/selector-specificity": ["@csstools/selector-specificity@6.0.0", "", { "peerDependencies": { "postcss-selector-parser": "^7.1.1" } }, "sha512-4sSgl78OtOXEX/2d++8A83zHNTgwCJMaR24FvsYL7Uf/VS8HZk9PTwR51elTbGqMuwH3szLvvOXEaVnqn0Z3zA=="], + + "@csstools/utilities": ["@csstools/utilities@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-etDqA/4jYvOGBM6yfKCOsEXfH96BKztZdgGmGqKi2xHnDe0ILIBraRspwgYatJH9JsCZ5HCGoCst8w18EKOAdg=="], "@dabh/diagnostics": ["@dabh/diagnostics@2.0.3", "", { "dependencies": { "colorspace": "1.1.x", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA=="], @@ -1063,55 +1137,57 @@ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.1", "", { "os": "android", "cpu": "arm" }, "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.1", "", { "os": "android", "cpu": "arm64" }, "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.1", "", { "os": "android", "cpu": "x64" }, "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.1", "", { "os": "linux", "cpu": "arm" }, "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.1", "", { "os": "linux", "cpu": "none" }, "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.1", "", { "os": "none", "cpu": "arm64" }, "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.1", "", { "os": "none", "cpu": "x64" }, "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], @@ -1125,7 +1201,7 @@ "@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="], - "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.5", "", { "dependencies": { "ajv": "^6.14.0", "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.5", "strip-json-comments": "^3.1.1" } }, "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg=="], "@eslint/js": ["@eslint/js@9.39.1", "", {}, "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw=="], @@ -1231,17 +1307,19 @@ "@floating-ui/utils": ["@floating-ui/utils@0.2.8", "", {}, "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig=="], - "@google/generative-ai": ["@google/generative-ai@0.24.0", "", {}, "sha512-fnEITCGEB7NdX0BhoYZ/cq/7WPZ1QS5IzJJfC3Tg/OwkvBetMiVJciyaan297OvE4B9Jg1xvo0zIazX/9sGu1Q=="], + "@google/genai": ["@google/genai@1.44.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-kRt9ZtuXmz+tLlcNntN/VV4LRdpl6ZOu5B1KbfNgfR65db15O6sUQcwnwLka8sT/V6qysD93fWrgJHF2L7dA9A=="], - "@googleapis/youtube": ["@googleapis/youtube@20.0.0", "", { "dependencies": { "googleapis-common": "^7.0.0" } }, "sha512-wdt1J0JoKYhvpoS2XIRHX0g/9ul/B0fQeeJAhuuBIdYINuuLt6/oZYZZCBmkuhtkA3IllXgqgAXOjLtLRAnR2g=="], + "@google/generative-ai": ["@google/generative-ai@0.24.0", "", {}, "sha512-fnEITCGEB7NdX0BhoYZ/cq/7WPZ1QS5IzJJfC3Tg/OwkvBetMiVJciyaan297OvE4B9Jg1xvo0zIazX/9sGu1Q=="], "@grpc/grpc-js": ["@grpc/grpc-js@1.9.15", "", { "dependencies": { "@grpc/proto-loader": "^0.7.8", "@types/node": ">=12.12.47" } }, "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ=="], "@grpc/proto-loader": ["@grpc/proto-loader@0.7.13", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.2.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw=="], + "@happy-dom/jest-environment": ["@happy-dom/jest-environment@20.8.3", "", { "dependencies": { "happy-dom": "^20.8.3" }, "peerDependencies": { "@jest/environment": ">=25.0.0", "@jest/fake-timers": ">=25.0.0", "@jest/types": ">=25.0.0", "jest-mock": ">=25.0.0", "jest-util": ">=25.0.0" } }, "sha512-VMOfNvF7UPPHIc7SUrFqGXqJrkONYX6Vd0ZXblmjgb1JA2RFnrc1KiVodzG0c7IT5Q0jfA0CQjvlqWjQ/BYtkQ=="], + "@headlessui/react": ["@headlessui/react@2.2.4", "", { "dependencies": { "@floating-ui/react": "^0.26.16", "@react-aria/focus": "^3.20.2", "@react-aria/interactions": "^3.25.0", "@tanstack/react-virtual": "^3.13.9", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-lz+OGcAH1dK93rgSMzXmm1qKOJkBUqZf1L4M8TWLNplftQD3IkoEDdUFNfAn4ylsN6WOTVtWaLmvmaHOUk1dTA=="], - "@hono/node-server": ["@hono/node-server@1.19.7", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw=="], + "@hono/node-server": ["@hono/node-server@1.19.11", "", { "peerDependencies": { "hono": "^4" } }, "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], @@ -1251,6 +1329,10 @@ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], + "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], + + "@iconify/utils": ["@iconify/utils@3.1.0", "", { "dependencies": { "@antfu/install-pkg": "^1.1.0", "@iconify/types": "^2.0.0", "mlly": "^1.8.0" } }, "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw=="], + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], @@ -1315,7 +1397,7 @@ "@jest/expect-utils": ["@jest/expect-utils@29.7.0", "", { "dependencies": { "jest-get-type": "^29.6.3" } }, "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA=="], - "@jest/fake-timers": ["@jest/fake-timers@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", "jest-message-util": "^29.7.0", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ=="], + "@jest/fake-timers": ["@jest/fake-timers@30.2.0", "", { "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" } }, "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw=="], "@jest/get-type": ["@jest/get-type@30.1.0", "", {}, "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA=="], @@ -1359,7 +1441,7 @@ "@langchain/aws": ["@langchain/aws@0.1.15", "", { "dependencies": { "@aws-sdk/client-bedrock-agent-runtime": "^3.755.0", "@aws-sdk/client-bedrock-runtime": "^3.840.0", "@aws-sdk/client-kendra": "^3.750.0", "@aws-sdk/credential-provider-node": "^3.750.0" }, "peerDependencies": { "@langchain/core": ">=0.3.58 <0.4.0" } }, "sha512-oyOMhTHP0rxdSCVI/g5KXYCOs9Kq/FpXMZbOk1JSIUoaIzUg4p6d98lsHu7erW//8NSaT+SX09QRbVDAgt7pNA=="], - "@langchain/core": ["@langchain/core@0.3.79", "", { "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.67", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^10.0.0", "zod": "^3.25.32", "zod-to-json-schema": "^3.22.3" } }, "sha512-ZLAs5YMM5N2UXN3kExMglltJrKKoW7hs3KMZFlXUnD7a5DFKBYxPFMeXA4rT+uvTxuJRZPCYX0JKI5BhyAWx4A=="], + "@langchain/core": ["@langchain/core@0.3.80", "", { "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.67", "mustache": "^4.2.0", "p-queue": "^6.6.2", "p-retry": "4", "uuid": "^10.0.0", "zod": "^3.25.32", "zod-to-json-schema": "^3.22.3" } }, "sha512-vcJDV2vk1AlCwSh3aBm/urQ1ZrlXFFBocv11bz/NBUfLWD5/UDNMzwPdaAd2dKvNmTWa9FM2lirLU3+JCf4cRA=="], "@langchain/deepseek": ["@langchain/deepseek@0.0.2", "", { "dependencies": { "@langchain/openai": "^0.5.5" }, "peerDependencies": { "@langchain/core": ">=0.3.58 <0.4.0" } }, "sha512-u13KbPUXW7uhcybbRzYdRroBgqVUSgG0SJM15c7Etld2yjRQC2c4O/ga9eQZdLh/kaDlQfH/ZITFdjHe77RnGw=="], @@ -1405,7 +1487,7 @@ "@lezer/lr": ["@lezer/lr@1.4.2", "", { "dependencies": { "@lezer/common": "^1.0.0" } }, "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA=="], - "@librechat/agents": ["@librechat/agents@3.0.50", "", { "dependencies": { "@langchain/anthropic": "^0.3.26", "@langchain/aws": "^0.1.15", "@langchain/core": "^0.3.79", "@langchain/deepseek": "^0.0.2", "@langchain/google-genai": "^0.2.18", "@langchain/google-vertexai": "^0.2.18", "@langchain/langgraph": "^0.4.9", "@langchain/mistralai": "^0.2.1", "@langchain/openai": "0.5.18", "@langchain/textsplitters": "^0.1.0", "@langchain/xai": "^0.0.3", "@langfuse/langchain": "^4.3.0", "@langfuse/otel": "^4.3.0", "@langfuse/tracing": "^4.3.0", "@opentelemetry/sdk-node": "^0.207.0", "axios": "^1.12.1", "cheerio": "^1.0.0", "dotenv": "^16.4.7", "https-proxy-agent": "^7.0.6", "mathjs": "^15.1.0", "nanoid": "^3.3.7", "openai": "5.8.2" } }, "sha512-oovj3BsP/QoxPbWFAc71Ddplwd9BT8ucfYs+n+kiR37aCWtvxdvL9/XldRYfnaq9boNE324njQJyqc8v8AAPFQ=="], + "@librechat/agents": ["@librechat/agents@3.1.56", "", { "dependencies": { "@anthropic-ai/sdk": "^0.73.0", "@aws-sdk/client-bedrock-runtime": "^3.980.0", "@langchain/anthropic": "^0.3.26", "@langchain/aws": "^0.1.15", "@langchain/core": "^0.3.80", "@langchain/deepseek": "^0.0.2", "@langchain/google-genai": "^0.2.18", "@langchain/google-vertexai": "^0.2.18", "@langchain/langgraph": "^0.4.9", "@langchain/mistralai": "^0.2.1", "@langchain/openai": "0.5.18", "@langchain/textsplitters": "^0.1.0", "@langchain/xai": "^0.0.3", "@langfuse/langchain": "^4.3.0", "@langfuse/otel": "^4.3.0", "@langfuse/tracing": "^4.3.0", "@opentelemetry/sdk-node": "^0.207.0", "@scarf/scarf": "^1.4.0", "ai-tokenizer": "^1.0.6", "axios": "^1.13.5", "cheerio": "^1.0.0", "dotenv": "^16.4.7", "https-proxy-agent": "^7.0.6", "mathjs": "^15.1.0", "nanoid": "^3.3.7", "okapibm25": "^1.4.1", "openai": "5.8.2" } }, "sha512-HJJwRnLM4XKpTWB4/wPDJR+iegyKBVUwqj7A8QHqzEcHzjKJDTr3wBPxZVH1tagGr6/mbbnErOJ14cH1OSNmpA=="], "@librechat/api": ["@librechat/api@workspace:packages/api"], @@ -1421,14 +1503,44 @@ "@mcp-ui/client": ["@mcp-ui/client@5.7.0", "", { "dependencies": { "@modelcontextprotocol/sdk": "*", "@quilted/threads": "^3.1.3", "@r2wc/react-to-web-component": "^2.0.4", "@remote-dom/core": "^1.8.0", "@remote-dom/react": "^1.2.2", "react": "^18.3.1", "react-dom": "^18.3.1" } }, "sha512-+HbPw3VS46WUSWmyJ34ZVnygb81QByA3luR6y0JDbyDZxjYtHw1FcIN7v9WbbE8PrfI0WcuWCSiNOO6sOGbwpQ=="], + "@mermaid-js/parser": ["@mermaid-js/parser@1.0.1", "", { "dependencies": { "langium": "^4.0.0" } }, "sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ=="], + "@microsoft/microsoft-graph-client": ["@microsoft/microsoft-graph-client@3.0.7", "", { "dependencies": { "@babel/runtime": "^7.12.5", "tslib": "^2.2.0" } }, "sha512-/AazAV/F+HK4LIywF9C+NYHcJo038zEnWkteilcxC1FM/uK/4NVGDKGrxx7nNq1ybspAroRKT4I1FHfxQzxkUw=="], "@mistralai/mistralai": ["@mistralai/mistralai@1.10.0", "", { "dependencies": { "zod": "^3.20.0", "zod-to-json-schema": "^3.24.1" } }, "sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg=="], - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.0", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-z0Zhn/LmQ3yz91dEfd5QgS7DpSjA4pk+3z2++zKgn5L6iDFM9QapsVoAQSbKLvlrFsZk9+ru6yHHWNq2lCYJKQ=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA=="], + + "@monaco-editor/loader": ["@monaco-editor/loader@1.7.0", "", { "dependencies": { "state-local": "^1.0.6" } }, "sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA=="], + + "@monaco-editor/react": ["@monaco-editor/react@4.7.0", "", { "dependencies": { "@monaco-editor/loader": "^1.5.0" }, "peerDependencies": { "monaco-editor": ">= 0.25.0 < 1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA=="], "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.3.1", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-6nZrq5kfAz0POWyhljnbWQQJQ5uT8oE2ddX303q1uY0tWsivWKgBDXBBvuFPwOqRRalXJuVO9EjOdVtuhLX0zg=="], + "@napi-rs/canvas": ["@napi-rs/canvas@0.1.96", "", { "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.96", "@napi-rs/canvas-darwin-arm64": "0.1.96", "@napi-rs/canvas-darwin-x64": "0.1.96", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.96", "@napi-rs/canvas-linux-arm64-gnu": "0.1.96", "@napi-rs/canvas-linux-arm64-musl": "0.1.96", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.96", "@napi-rs/canvas-linux-x64-gnu": "0.1.96", "@napi-rs/canvas-linux-x64-musl": "0.1.96", "@napi-rs/canvas-win32-arm64-msvc": "0.1.96", "@napi-rs/canvas-win32-x64-msvc": "0.1.96" } }, "sha512-6NNmNxvoJKeucVjxaaRUt3La2i5jShgiAbaY3G/72s1Vp3U06XPrAIxkAjBxpDcamEn/t+WJ4OOlGmvILo4/Ew=="], + + "@napi-rs/canvas-android-arm64": ["@napi-rs/canvas-android-arm64@0.1.96", "", { "os": "android", "cpu": "arm64" }, "sha512-ew1sPrN3dGdZ3L4FoohPfnjq0f9/Jk7o+wP7HkQZokcXgIUD6FIyICEWGhMYzv53j63wUcPvZeAwgewX58/egg=="], + + "@napi-rs/canvas-darwin-arm64": ["@napi-rs/canvas-darwin-arm64@0.1.96", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Q/wOXZ5PzTqpdmA5eUOcegCf4Go/zz3aZ5DlzSeDpOjFmfwMKh8EzLAoweQ+mJVagcHQyzoJhaTEnrO68TNyNg=="], + + "@napi-rs/canvas-darwin-x64": ["@napi-rs/canvas-darwin-x64@0.1.96", "", { "os": "darwin", "cpu": "x64" }, "sha512-UrXiQz28tQEvGM1qvyptewOAfmUrrd5+wvi6Rzjj2VprZI8iZ2KIvBD2lTTG1bVF95AbeDeG7PJA0D9sLKaOFA=="], + + "@napi-rs/canvas-linux-arm-gnueabihf": ["@napi-rs/canvas-linux-arm-gnueabihf@0.1.96", "", { "os": "linux", "cpu": "arm" }, "sha512-I90ODxweD8aEP6XKU/NU+biso95MwCtQ2F46dUvhec1HesFi0tq/tAJkYic/1aBSiO/1kGKmSeD1B0duOHhEHQ=="], + + "@napi-rs/canvas-linux-arm64-gnu": ["@napi-rs/canvas-linux-arm64-gnu@0.1.96", "", { "os": "linux", "cpu": "arm64" }, "sha512-Dx/0+RFV++w3PcRy+4xNXkghhXjA5d0Mw1bs95emn5Llinp1vihMaA6WJt3oYv2LAHc36+gnrhIBsPhUyI2SGw=="], + + "@napi-rs/canvas-linux-arm64-musl": ["@napi-rs/canvas-linux-arm64-musl@0.1.96", "", { "os": "linux", "cpu": "arm64" }, "sha512-UvOi7fii3IE2KDfEfhh8m+LpzSRvhGK7o1eho99M2M0HTik11k3GX+2qgVx9EtujN3/bhFFS1kSO3+vPMaJ0Mg=="], + + "@napi-rs/canvas-linux-riscv64-gnu": ["@napi-rs/canvas-linux-riscv64-gnu@0.1.96", "", { "os": "linux", "cpu": "none" }, "sha512-MBSukhGCQ5nRtf9NbFYWOU080yqkZU1PbuH4o1ROvB4CbPl12fchDR35tU83Wz8gWIM9JTn99lBn9DenPIv7Ig=="], + + "@napi-rs/canvas-linux-x64-gnu": ["@napi-rs/canvas-linux-x64-gnu@0.1.96", "", { "os": "linux", "cpu": "x64" }, "sha512-I/ccu2SstyKiV3HIeVzyBIWfrJo8cN7+MSQZPnabewWV6hfJ2nY7Df2WqOHmobBRUw84uGR6zfQHsUEio/m5Vg=="], + + "@napi-rs/canvas-linux-x64-musl": ["@napi-rs/canvas-linux-x64-musl@0.1.96", "", { "os": "linux", "cpu": "x64" }, "sha512-H3uov7qnTl73GDT4h52lAqpJPsl1tIUyNPWJyhQ6gHakohNqqRq3uf80+NEpzcytKGEOENP1wX3yGwZxhjiWEQ=="], + + "@napi-rs/canvas-win32-arm64-msvc": ["@napi-rs/canvas-win32-arm64-msvc@0.1.96", "", { "os": "win32", "cpu": "arm64" }, "sha512-ATp6Y+djOjYtkfV/VRH7CZ8I1MEtkUQBmKUbuWw5zWEHHqfL0cEcInE4Cxgx7zkNAhEdBbnH8HMVrqNp+/gwxA=="], + + "@napi-rs/canvas-win32-x64-msvc": ["@napi-rs/canvas-win32-x64-msvc@0.1.96", "", { "os": "win32", "cpu": "x64" }, "sha512-UYGdTltVd+Z8mcIuoqGmAXXUvwH5CLf2M6mIB5B0/JmX5J041jETjqtSYl7gN+aj3k1by/SG6sS0hAwCqyK7zw=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], @@ -1547,7 +1659,7 @@ "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-collapsible": "1.1.11", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A=="], - "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw=="], + "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.0", "@radix-ui/react-compose-refs": "1.0.0", "@radix-ui/react-context": "1.0.0", "@radix-ui/react-dialog": "1.0.2", "@radix-ui/react-primitive": "1.0.1", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0MtxV53FaEEBOKRgyLnEqHZKKDS5BldQ9oUBsKVXWI5FHbl2jp35qs+0aJET+K5hJDsc40kQUzP7g+wC7tqrqA=="], "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA=="], @@ -1561,17 +1673,17 @@ "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], - "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.0", "@radix-ui/react-compose-refs": "1.0.0", "@radix-ui/react-context": "1.0.0", "@radix-ui/react-dismissable-layer": "1.0.2", "@radix-ui/react-focus-guards": "1.0.0", "@radix-ui/react-focus-scope": "1.0.1", "@radix-ui/react-id": "1.0.0", "@radix-ui/react-portal": "1.0.1", "@radix-ui/react-presence": "1.0.0", "@radix-ui/react-primitive": "1.0.1", "@radix-ui/react-slot": "1.0.1", "@radix-ui/react-use-controllable-state": "1.0.0", "aria-hidden": "^1.1.1", "react-remove-scroll": "2.5.5" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-EKxxp2WNSmUPkx4trtWNmZ4/vAYEg7JkAfa1HKBUnaubw9eHzf1Orr9B472lJYaYz327RHDrd4R95fsw7VR8DA=="], "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], - "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.0", "@radix-ui/react-compose-refs": "1.0.0", "@radix-ui/react-primitive": "1.0.1", "@radix-ui/react-use-callback-ref": "1.0.0", "@radix-ui/react-use-escape-keydown": "1.0.2" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-WjJzMrTWROozDqLB0uRWYvj4UuXsM/2L19EmQ3Au+IJWqwvwq9Bwd+P8ivo0Deg9JDPArR1I6MbWNi1CmXsskg=="], "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.1", "", { "dependencies": { "@radix-ui/primitive": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-context": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-menu": "2.1.1", "@radix-ui/react-primitive": "2.0.0", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ=="], - "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ=="], - "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0", "@radix-ui/react-primitive": "1.0.1", "@radix-ui/react-use-callback-ref": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-Ej2MQTit8IWJiS2uuujGUmxXjF/y5xZptIIQnyd2JHLwtV0R2j9NRVoRj/1j/gJ7e3REdaBw4Hjf4a1ImhkZcQ=="], "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.0.7", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-dismissable-layer": "1.0.5", "@radix-ui/react-popper": "1.1.3", "@radix-ui/react-portal": "1.0.4", "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-controllable-state": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-OcUN2FU0YpmajD/qkph3XzMcK/NmSk9hGWnjV68p6QiZMgILugusgQwnLSDs3oFSJYGKf3Y49zgFedhGh04k9A=="], @@ -1587,7 +1699,7 @@ "@radix-ui/react-popper": ["@radix-ui/react-popper@1.1.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.0.3", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-callback-ref": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1", "@radix-ui/react-use-rect": "1.0.1", "@radix-ui/react-use-size": "1.0.1", "@radix-ui/rect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w=="], - "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-NY2vUWI5WENgAT1nfC6JS7RU5xRYBfjZVLq0HmgEN1Ezy3rk/UruMV4+Rd0F40PEaFC5SrLS1ixYvcYIQrb4Ig=="], "@radix-ui/react-presence": ["@radix-ui/react-presence@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-use-layout-effect": "1.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg=="], @@ -1619,7 +1731,7 @@ "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], - "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA=="], "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], @@ -1673,7 +1785,7 @@ "@redis/client": ["@redis/client@1.6.0", "", { "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", "yallist": "4.0.0" } }, "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg=="], - "@remix-run/router": ["@remix-run/router@1.15.0", "", {}, "sha512-HOil5aFtme37dVQTB6M34G95kPM3MMuqSmIRVCC52eKV+Y/tGSqw9P3rWhlAx6A+mz+MoX+XxsGsNJbaI5qCgQ=="], + "@remix-run/router": ["@remix-run/router@1.23.2", "", {}, "sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w=="], "@remote-dom/core": ["@remote-dom/core@1.9.0", "", { "dependencies": { "@remote-dom/polyfill": "^1.4.4", "htm": "^3.1.1" }, "peerDependencies": { "@preact/signals-core": "^1.3.0" } }, "sha512-h8OO2NRns2paXO/q5hkfXrwlZKq7oKj9XedGosi7J8OP3+aW7N2Gv4MBBVVQGCfOiZPkOj5m3sQH7FdyUWl7PQ=="], @@ -1681,6 +1793,8 @@ "@remote-dom/react": ["@remote-dom/react@1.2.2", "", { "dependencies": { "@remote-dom/core": "^1.7.0", "@types/react": "^18.0.0", "htm": "^3.1.1" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0" } }, "sha512-PkvioODONTr1M0StGDYsR4Ssf5M0Rd4+IlWVvVoK3Zrw8nr7+5mJkgNofaj/z7i8Aep78L28PCW8/WduUt4unA=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], + "@rollup/plugin-alias": ["@rollup/plugin-alias@5.1.0", "", { "dependencies": { "slash": "^4.0.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" } }, "sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ=="], "@rollup/plugin-babel": ["@rollup/plugin-babel@5.3.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.10.4", "@rollup/pluginutils": "^3.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0", "@types/babel__core": "^7.1.9", "rollup": "^1.20.0||^2.0.0" } }, "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q=="], @@ -1701,155 +1815,171 @@ "@rollup/pluginutils": ["@rollup/pluginutils@5.1.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^2.3.1" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" } }, "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.37.0", "", { "os": "android", "cpu": "arm" }, "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.37.0", "", { "os": "android", "cpu": "arm64" }, "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.37.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.59.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.37.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.59.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.37.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.59.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.37.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.59.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.37.0", "", { "os": "linux", "cpu": "arm" }, "sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.37.0", "", { "os": "linux", "cpu": "arm" }, "sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.37.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.37.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q=="], "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA=="], "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.37.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA=="], + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.37.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.37.0", "", { "os": "linux", "cpu": "x64" }, "sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.37.0", "", { "os": "linux", "cpu": "x64" }, "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.59.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.37.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.37.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.37.0", "", { "os": "win32", "cpu": "x64" }, "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA=="], + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.59.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.59.0", "", { "os": "none", "cpu": "arm64" }, "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.59.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.59.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="], "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], + "@scarf/scarf": ["@scarf/scarf@1.4.0", "", {}, "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ=="], + "@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="], "@sinonjs/commons": ["@sinonjs/commons@3.0.1", "", { "dependencies": { "type-detect": "4.0.8" } }, "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ=="], - "@sinonjs/fake-timers": ["@sinonjs/fake-timers@10.3.0", "", { "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA=="], + "@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="], - "@smithy/abort-controller": ["@smithy/abort-controller@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-P7JD4J+wxHMpGxqIg6SHno2tPkZbBUBLbPpR5/T1DEUvw/mEaINBMaPFZNM7lA+ToSCZ36j6nMHa+5kej+fhGg=="], + "@smithy/abort-controller": ["@smithy/abort-controller@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-Hj4WoYWMJnSpM6/kchsm4bUNTL9XiSyhvoMb2KIq4VJzyDt7JpGHUZHkVNPZVC7YE1tf8tPeVauxpFBKGW4/KQ=="], - "@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw=="], + "@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw=="], - "@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.0.0", "", { "dependencies": { "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig=="], + "@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.2.3", "", { "dependencies": { "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw=="], - "@smithy/config-resolver": ["@smithy/config-resolver@4.0.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ=="], + "@smithy/config-resolver": ["@smithy/config-resolver@4.4.10", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.11", "@smithy/types": "^4.13.0", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-IRTkd6ps0ru+lTWnfnsbXzW80A8Od8p3pYiZnW98K2Hb20rqfsX7VTlfUwhrcOeSSy68Gn9WBofwPuw3e5CCsg=="], - "@smithy/core": ["@smithy/core@3.1.5", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.2", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA=="], + "@smithy/core": ["@smithy/core@3.23.9", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.12", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-1Vcut4LEL9HZsdpI0vFiRYIsaoPwZLjAxnVQDUMQK8beMS+EYPLDQCXtbzfxmM5GzSgjfe2Q9M7WaXwIMQllyQ=="], "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@3.2.0", "", { "dependencies": { "@smithy/node-config-provider": "^3.1.4", "@smithy/property-provider": "^3.1.3", "@smithy/types": "^3.3.0", "@smithy/url-parser": "^3.0.3", "tslib": "^2.6.2" } }, "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA=="], - "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.6", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-OZfsI+YRG26XZik/jKMMg37acnBSbUiK/8nETW3uM3mLj+0tMmFXdHQw1e5WEd/IHN8BGOh3te91SNDe2o4RHg=="], + "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.11", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.13.0", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-Sf39Ml0iVX+ba/bgMPxaXWAAFmHqYLTmbjAPfLPLY8CrYkRDEqZdUsKC1OwVMCdJXfAt0v4j49GIJ8DoSYAe6w=="], - "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-d5T7ZS3J/r8P/PDjgmCcutmNxnSRvPH1U6iHeXjzI50sMr78GLmFcrczLw33Ap92oEKqa4CLrkAPeSSOqvGdUA=="], + "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.11", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-3rEpo3G6f/nRS7fQDsZmxw/ius6rnlIpz4UX6FlALEzz8JoSxFmdBt0SZnthis+km7sQo6q5/3e+UJcuQivoXA=="], - "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-lxfDT0UuSc1HqltOGsTEAlZ6H29gpfDSdEPTapD5G63RbnYToZ+ezjzdonCCH90j5tRRCw3aLXVbiZaBW3VRVg=="], + "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-XeNIA8tcP/GDWnnKkO7qEm/bg0B/bP9lvIXZBXcGZwZ+VYM8h8k9wuDvUODtdQ2Wcp2RcBkPTCSMmaniVHrMlA=="], - "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-TPhiGByWnYyzcpU/K3pO5V7QgtXYpE0NaJPEZBCa1Y5jlw5SjqzMSbFiLb+ZkJhqoQc0ImGyVINqnq1ze0ZRcQ=="], + "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.11", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-fzbCh18rscBDTQSCrsp1fGcclLNF//nJyhjldsEl/5wCYmgpHblv5JSppQAyQI24lClsFT0wV06N1Porn0IsEw=="], - "@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ=="], + "@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.11", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-MJ7HcI+jEkqoWT5vp+uoVaAjBrmxBtKhZTeynDRG/seEjJfqyg3SiqMMqyPnAMzmIfLaeJ/uiuSDP/l9AnMy/Q=="], - "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.0.1", "", { "dependencies": { "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA=="], + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.13", "", { "dependencies": { "@smithy/protocol-http": "^5.3.11", "@smithy/querystring-builder": "^4.2.11", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-U2Hcfl2s3XaYjikN9cT4mPu8ybDbImV3baXR0PkVlC0TTx808bRP3FaPGAzPtB8OByI+JqJ1kyS+7GEgae7+qQ=="], - "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.0.1", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.0.0", "@smithy/chunked-blob-reader-native": "^4.0.0", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-rkFIrQOKZGS6i1D3gKJ8skJ0RlXqDvb1IyAphksaFOMzkn3v3I1eJ8m7OkLj0jf1McP63rcCEoLlkAn/HjcTRw=="], + "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.2.12", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.2.2", "@smithy/chunked-blob-reader-native": "^4.2.3", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-1wQE33DsxkM/waftAhCH9VtJbUGyt1PJ9YRDpOu+q9FUi73LLFUZ2fD8A61g2mT1UY9k7b99+V1xZ41Rz4SHRQ=="], - "@smithy/hash-node": ["@smithy/hash-node@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w=="], + "@smithy/hash-node": ["@smithy/hash-node@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-T+p1pNynRkydpdL015ruIoyPSRw9e/SQOWmSAMmmprfswMrd5Ow5igOWNVlvyVFZlxXqGmyH3NQwfwy8r5Jx0A=="], - "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-U1rAE1fxmReCIr6D2o/4ROqAQX+GffZpyMt3d7njtGDr2pUNmAKRWa49gsNVhCh2vVAuf3wXzWwNr2YN8PAXIw=="], + "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-hQsTjwPCRY8w9GK07w1RqJi3e+myh0UaOWBBhZ1UMSDgofH/Q1fEYzU1teaX6HkpX/eWDdm7tAGR0jBPlz9QEQ=="], - "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ=="], + "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-cGNMrgykRmddrNhYy1yBdrp5GwIgEkniS7k9O1VLB38yxQtlvrxpZtUVvo6T4cKpeZsriukBuuxfJcdZQc/f/g=="], - "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow=="], - "@smithy/md5-js": ["@smithy/md5-js@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-HLZ647L27APi6zXkZlzSFZIjpo8po45YiyjMGJZM3gyDY8n7dPGdmxIIljLm4gPt/7rRvutLTTkYJpZVfG5r+A=="], + "@smithy/md5-js": ["@smithy/md5-js@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-350X4kGIrty0Snx2OWv7rPM6p6vM7RzryvFs6B/56Cux3w3sChOb3bymo5oidXJlPcP9fIRxGUCk7GqpiSOtng=="], - "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.0.1", "", { "dependencies": { "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ=="], + "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.11", "", { "dependencies": { "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-UvIfKYAKhCzr4p6jFevPlKhQwyQwlJ6IeKLDhmV1PlYfcW3RL4ROjNEDtSik4NYMi9kDkH7eSwyTP3vNJ/u/Dw=="], - "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.0.6", "", { "dependencies": { "@smithy/core": "^3.1.5", "@smithy/middleware-serde": "^4.0.2", "@smithy/node-config-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-ftpmkTHIFqgaFugcjzLZv3kzPEFsBFSnq1JsIkr2mwFzCraZVhQk2gqN51OOeRxqhbPTkRFj39Qd2V91E/mQxg=="], + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.23", "", { "dependencies": { "@smithy/core": "^3.23.9", "@smithy/middleware-serde": "^4.2.12", "@smithy/node-config-provider": "^4.3.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-middleware": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-UEFIejZy54T1EJn2aWJ45voB7RP2T+IRzUqocIdM6GFFa5ClZncakYJfcYnoXt3UsQrZZ9ZRauGm77l9UCbBLw=="], - "@smithy/middleware-retry": ["@smithy/middleware-retry@4.0.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/service-error-classification": "^4.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-58j9XbUPLkqAcV1kHzVX/kAR16GT+j7DUZJqwzsxh1jtz7G82caZiGyyFgUvogVfNTg3TeAOIJepGc8TXF4AVQ=="], + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.40", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.11", "@smithy/protocol-http": "^5.3.11", "@smithy/service-error-classification": "^4.2.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-YhEMakG1Ae57FajERdHNZ4ShOPIY7DsgV+ZoAxo/5BT0KIe+f6DDU2rtIymNNFIj22NJfeeI6LWIifrwM0f+rA=="], - "@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.2", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ=="], + "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.12", "", { "dependencies": { "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-W9g1bOLui7Xn5FABRVS0o3rXL0gfN37d/8I/W7i0N7oxjx9QecUmXEMSUMADTODwdtka9cN43t5BI2CodLJpng=="], - "@smithy/middleware-stack": ["@smithy/middleware-stack@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA=="], + "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-s+eenEPW6RgliDk2IhjD2hWOxIx1NKrOHxEwNUaUXxYBxIyCcDfNULZ2Mu15E3kwcJWBedTET/kEASPV1A1Akg=="], - "@smithy/node-config-provider": ["@smithy/node-config-provider@4.0.1", "", { "dependencies": { "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ=="], + "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.11", "", { "dependencies": { "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-xD17eE7kaLgBBGf5CZQ58hh2YmwK1Z0O8YhffwB/De2jsL0U3JklmhVYJ9Uf37OtUDLF2gsW40Xwwag9U869Gg=="], - "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.6", "", { "dependencies": { "@smithy/abort-controller": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-Gsb9jf4ido5BhPfani4ggyrKDd3ZK+vTFWmUaZeFg5G3E5nhFmqiTzAIbHqmPs1sARuJawDiGMGR/nY+Gw6+aQ=="], + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.14", "", { "dependencies": { "@smithy/abort-controller": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/querystring-builder": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-DamSqaU8nuk0xTJDrYnRzZndHwwRnyj/n/+RqGGCcBKB4qrQem0mSDiWdupaNWdwxzyMU91qxDmHOCazfhtO3A=="], "@smithy/property-provider": ["@smithy/property-provider@3.1.3", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g=="], - "@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + "@smithy/protocol-http": ["@smithy/protocol-http@5.3.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-hI+barOVDJBkNt4y0L2mu3Ugc0w7+BpJ2CZuLwXtSltGAAwCb3IvnalGlbDV/UCS6a9ZuT3+exd1WxNdLb5IlQ=="], - "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-MeM9fTAiD3HvoInK/aA8mgJaKQDvm8N0dKy6EiFaCfgpovQr4CaOkJC28XqlSRABM+sHdSQXbC8NZ0DShBMHqg=="], + "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-7spdikrYiljpket6u0up2Ck2mxhy7dZ0+TDd+S53Dg2DHd6wg+YNJrTCHiLdgZmEXZKI7LJZcwL3721ZRDFiqA=="], - "@smithy/querystring-parser": ["@smithy/querystring-parser@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw=="], + "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-nE3IRNjDltvGcoThD2abTozI1dkSy8aX+a2N1Rs55en5UsdyyIXgGEmevUL3okZFoJC77JgRGe99xYohhsjivQ=="], - "@smithy/service-error-classification": ["@smithy/service-error-classification@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0" } }, "sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA=="], + "@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0" } }, "sha512-HkMFJZJUhzU3HvND1+Yw/kYWXp4RPDLBWLcK1n+Vqw8xn4y2YiBhdww8IxhkQjP/QlZun5bwm3vcHc8AqIU3zw=="], - "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw=="], + "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.6", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-IB/M5I8G0EeXZTHsAxpx51tMQ5R719F3aq+fjEB6VtNcCHDc0ajFDIGDZw+FW9GxtEkgTduiPpjveJdA/CX7sw=="], - "@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + "@smithy/signature-v4": ["@smithy/signature-v4@5.3.11", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-uri-escape": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-V1L6N9aKOBAN4wEHLyqjLBnAz13mtILU0SeDrjOaIZEeN6IFa6DxwRt1NNpOdmSpQUfkBj0qeD3m6P77uzMhgQ=="], - "@smithy/smithy-client": ["@smithy/smithy-client@4.1.6", "", { "dependencies": { "@smithy/core": "^3.1.5", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-stack": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-stream": "^4.1.2", "tslib": "^2.6.2" } }, "sha512-UYDolNg6h2O0L+cJjtgSyKKvEKCOa/8FHYJnBobyeoeWDmNpXjwOAtw16ezyeu1ETuuLEOZbrynK0ZY1Lx9Jbw=="], + "@smithy/smithy-client": ["@smithy/smithy-client@4.12.3", "", { "dependencies": { "@smithy/core": "^3.23.9", "@smithy/middleware-endpoint": "^4.4.23", "@smithy/middleware-stack": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-stream": "^4.5.17", "tslib": "^2.6.2" } }, "sha512-7k4UxjSpHmPN2AxVhvIazRSzFQjWnud3sOsXcFStzagww17j1cFQYqTSiQ8xuYK3vKLR1Ni8FzuT3VlKr3xCNw=="], - "@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="], + "@smithy/types": ["@smithy/types@4.13.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw=="], - "@smithy/url-parser": ["@smithy/url-parser@4.0.1", "", { "dependencies": { "@smithy/querystring-parser": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g=="], + "@smithy/url-parser": ["@smithy/url-parser@4.2.11", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-oTAGGHo8ZYc5VZsBREzuf5lf2pAurJQsccMusVZ85wDkX66ojEc/XauiGjzCj50A61ObFTPe6d7Pyt6UBYaing=="], - "@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="], + "@smithy/util-base64": ["@smithy/util-base64@4.3.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ=="], - "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="], + "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ=="], - "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg=="], + "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g=="], - "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], + "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.2", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q=="], - "@smithy/util-config-provider": ["@smithy/util-config-provider@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w=="], + "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ=="], - "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.0.7", "", { "dependencies": { "@smithy/property-provider": "^4.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-CZgDDrYHLv0RUElOsmZtAnp1pIjwDVCSuZWOPhIOBvG36RDfX1Q9+6lS61xBf+qqvHoqRjHxgINeQz47cYFC2Q=="], + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.39", "", { "dependencies": { "@smithy/property-provider": "^4.2.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-ui7/Ho/+VHqS7Km2wBw4/Ab4RktoiSshgcgpJzC4keFPs6tLJS4IQwbeahxQS3E/w98uq6E1mirCH/id9xIXeQ=="], - "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.0.7", "", { "dependencies": { "@smithy/config-resolver": "^4.0.1", "@smithy/credential-provider-imds": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-79fQW3hnfCdrfIi1soPbK3zmooRFnLpSx3Vxi6nUlqaaQeC5dm8plt4OTNDNqEEEDkvKghZSaoti684dQFVrGQ=="], + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.42", "", { "dependencies": { "@smithy/config-resolver": "^4.4.10", "@smithy/credential-provider-imds": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/property-provider": "^4.2.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-QDA84CWNe8Akpj15ofLO+1N3Rfg8qa2K5uX0y6HnOp4AnRYRgWrKx/xzbYNbVF9ZsyJUYOfcoaN3y93wA/QJ2A=="], - "@smithy/util-endpoints": ["@smithy/util-endpoints@3.0.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA=="], + "@smithy/util-endpoints": ["@smithy/util-endpoints@3.3.2", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-+4HFLpE5u29AbFlTdlKIT7jfOzZ8PDYZKTb3e+AgLz986OYwqTourQ5H+jg79/66DB69Un1+qKecLnkZdAsYcA=="], - "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg=="], - "@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@smithy/util-middleware": ["@smithy/util-middleware@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-r3dtF9F+TpSZUxpOVVtPfk09Rlo4lT6ORBqEvX3IBT6SkQAdDSVKR5GcfmZbtl7WKhKnmb3wbDTQ6ibR2XHClw=="], - "@smithy/util-retry": ["@smithy/util-retry@4.0.1", "", { "dependencies": { "@smithy/service-error-classification": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw=="], + "@smithy/util-retry": ["@smithy/util-retry@4.2.11", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-XSZULmL5x6aCTTii59wJqKsY1l3eMIAomRAccW7Tzh9r8s7T/7rdo03oektuH5jeYRlJMPcNP92EuRDvk9aXbw=="], - "@smithy/util-stream": ["@smithy/util-stream@4.1.2", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw=="], + "@smithy/util-stream": ["@smithy/util-stream@4.5.17", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.13", "@smithy/node-http-handler": "^4.4.14", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-793BYZ4h2JAQkNHcEnyFxDTcZbm9bVybD0UV/LEWmZ5bkTms7JqjfrLMi2Qy0E5WFcCzLwCAPgcvcvxoeALbAQ=="], - "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw=="], - "@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="], + "@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="], - "@smithy/util-waiter": ["@smithy/util-waiter@4.0.6", "", { "dependencies": { "@smithy/abort-controller": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-slcr1wdRbX7NFphXZOxtxRNA7hXAAtJAXJDE/wdoMAos27SIquVCKiSqfB6/28YzQ8FCsB5NKkhdM5gMADbqxg=="], + "@smithy/util-waiter": ["@smithy/util-waiter@4.2.11", "", { "dependencies": { "@smithy/abort-controller": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-x7Rh2azQPs3XxbvCzcttRErKKvLnbZfqRf/gOjw2pb+ZscX88e5UkRPCB67bVnsFHxayvMvmePfKTqsRb+is1A=="], - "@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@smithy/uuid": ["@smithy/uuid@1.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g=="], "@stitches/core": ["@stitches/core@1.2.8", "", {}, "sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg=="], @@ -1881,12 +2011,10 @@ "@testing-library/user-event": ["@testing-library/user-event@14.5.2", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ=="], + "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="], + "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], - "@tootallnate/once": ["@tootallnate/once@2.0.0", "", {}, "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="], - - "@trysound/sax": ["@trysound/sax@0.2.0", "", {}, "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="], - "@tsconfig/node10": ["@tsconfig/node10@1.0.11", "", {}, "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw=="], "@tsconfig/node12": ["@tsconfig/node12@1.0.11", "", {}, "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="], @@ -1913,9 +2041,69 @@ "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], - "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + "@types/d3": ["@types/d3@7.4.3", "", { "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": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="], - "@types/diff": ["@types/diff@6.0.0", "", {}, "sha512-dhVCYGv3ZSbzmQaBSagrv1WJ6rXCdkyTcDyoNu1MD8JohI7pR7k8wdZEm+mvdxRKXyHVwckFzWU1vJc+Z29MlA=="], + "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], + + "@types/d3-axis": ["@types/d3-axis@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw=="], + + "@types/d3-brush": ["@types/d3-brush@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A=="], + + "@types/d3-chord": ["@types/d3-chord@3.0.6", "", {}, "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="], + + "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], + + "@types/d3-contour": ["@types/d3-contour@3.0.6", "", { "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg=="], + + "@types/d3-delaunay": ["@types/d3-delaunay@6.0.4", "", {}, "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="], + + "@types/d3-dispatch": ["@types/d3-dispatch@3.0.7", "", {}, "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA=="], + + "@types/d3-drag": ["@types/d3-drag@3.0.7", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ=="], + + "@types/d3-dsv": ["@types/d3-dsv@3.0.7", "", {}, "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="], + + "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], + + "@types/d3-fetch": ["@types/d3-fetch@3.0.7", "", { "dependencies": { "@types/d3-dsv": "*" } }, "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA=="], + + "@types/d3-force": ["@types/d3-force@3.0.10", "", {}, "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="], + + "@types/d3-format": ["@types/d3-format@3.0.4", "", {}, "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="], + + "@types/d3-geo": ["@types/d3-geo@3.1.0", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ=="], + + "@types/d3-hierarchy": ["@types/d3-hierarchy@3.1.7", "", {}, "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="], + + "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], + + "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], + + "@types/d3-polygon": ["@types/d3-polygon@3.0.2", "", {}, "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="], + + "@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="], + + "@types/d3-random": ["@types/d3-random@3.0.3", "", {}, "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="], + + "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], + + "@types/d3-scale-chromatic": ["@types/d3-scale-chromatic@3.1.0", "", {}, "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="], + + "@types/d3-selection": ["@types/d3-selection@3.0.11", "", {}, "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w=="], + + "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="], + + "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], + + "@types/d3-time-format": ["@types/d3-time-format@4.0.3", "", {}, "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="], + + "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], + + "@types/d3-transition": ["@types/d3-transition@3.0.9", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg=="], + + "@types/d3-zoom": ["@types/d3-zoom@3.0.8", "", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="], + + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], "@types/estree": ["@types/estree@1.0.6", "", {}, "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="], @@ -1927,6 +2115,8 @@ "@types/express-session": ["@types/express-session@1.18.2", "", { "dependencies": { "@types/express": "*" } }, "sha512-k+I0BxwVXsnEU2hV77cCobC08kIsn4y44C3gC0b46uxZVMaXA04lSPgRLR/bSL2w0t0ShJiG8o4jPzRG/nscFg=="], + "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="], + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], "@types/http-errors": ["@types/http-errors@2.0.4", "", {}, "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA=="], @@ -1965,7 +2155,7 @@ "@types/multer": ["@types/multer@1.4.13", "", { "dependencies": { "@types/express": "*" } }, "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw=="], - "@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + "@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="], "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="], @@ -2011,10 +2201,14 @@ "@types/webidl-conversions": ["@types/webidl-conversions@7.0.3", "", {}, "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="], + "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="], + "@types/whatwg-url": ["@types/whatwg-url@11.0.5", "", { "dependencies": { "@types/webidl-conversions": "*" } }, "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ=="], "@types/winston": ["@types/winston@2.4.4", "", { "dependencies": { "winston": "*" } }, "sha512-BVGCztsypW8EYwJ+Hq+QNYiT/MUyCif0ouBH+flrY66O5W+KIXAMML6E/0fJpm7VjIzgangahl5S03bJJQGrZw=="], + "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "@types/xml-encryption": ["@types/xml-encryption@1.2.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-I69K/WW1Dv7j6O3jh13z0X8sLWJRXbu5xnHDl9yHzUNDUBtUoBY058eb5s+x/WG6yZC1h8aKdI2EoyEPjyEh+Q=="], "@types/xml2js": ["@types/xml2js@0.4.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ=="], @@ -2039,6 +2233,8 @@ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.24.0", "", { "dependencies": { "@typescript-eslint/types": "8.24.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-kArLq83QxGLbuHrTMoOEWO+l2MwsNS2TGISEdx8xgqpkbytB07XmlQyQdNDrCc1ecSqx0cnmhGvpX+VBwqqSkg=="], + "@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.4", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-CI0NhTrz4EBaa0U+HaaUZrJhPoso8sG7ZFya8uQoBA57fjzrjRSv87ekCjLZOFExN+gXE/z0xuN2QfH4H2HrLQ=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="], @@ -2079,48 +2275,14 @@ "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@4.3.4", "", { "dependencies": { "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.2" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug=="], + "@upsetjs/venn.js": ["@upsetjs/venn.js@2.0.0", "", { "optionalDependencies": { "d3-selection": "^3.0.0", "d3-transition": "^3.0.1" } }, "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw=="], - "@webassemblyjs/ast": ["@webassemblyjs/ast@1.12.1", "", { "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg=="], - - "@webassemblyjs/floating-point-hex-parser": ["@webassemblyjs/floating-point-hex-parser@1.11.6", "", {}, "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw=="], - - "@webassemblyjs/helper-api-error": ["@webassemblyjs/helper-api-error@1.11.6", "", {}, "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q=="], - - "@webassemblyjs/helper-buffer": ["@webassemblyjs/helper-buffer@1.12.1", "", {}, "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw=="], - - "@webassemblyjs/helper-numbers": ["@webassemblyjs/helper-numbers@1.11.6", "", { "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", "@xtuc/long": "4.2.2" } }, "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g=="], - - "@webassemblyjs/helper-wasm-bytecode": ["@webassemblyjs/helper-wasm-bytecode@1.11.6", "", {}, "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA=="], - - "@webassemblyjs/helper-wasm-section": ["@webassemblyjs/helper-wasm-section@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/wasm-gen": "1.12.1" } }, "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g=="], - - "@webassemblyjs/ieee754": ["@webassemblyjs/ieee754@1.11.6", "", { "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg=="], - - "@webassemblyjs/leb128": ["@webassemblyjs/leb128@1.11.6", "", { "dependencies": { "@xtuc/long": "4.2.2" } }, "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ=="], - - "@webassemblyjs/utf8": ["@webassemblyjs/utf8@1.11.6", "", {}, "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA=="], - - "@webassemblyjs/wasm-edit": ["@webassemblyjs/wasm-edit@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/helper-wasm-section": "1.12.1", "@webassemblyjs/wasm-gen": "1.12.1", "@webassemblyjs/wasm-opt": "1.12.1", "@webassemblyjs/wasm-parser": "1.12.1", "@webassemblyjs/wast-printer": "1.12.1" } }, "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g=="], - - "@webassemblyjs/wasm-gen": ["@webassemblyjs/wasm-gen@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", "@webassemblyjs/utf8": "1.11.6" } }, "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w=="], - - "@webassemblyjs/wasm-opt": ["@webassemblyjs/wasm-opt@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/wasm-gen": "1.12.1", "@webassemblyjs/wasm-parser": "1.12.1" } }, "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg=="], - - "@webassemblyjs/wasm-parser": ["@webassemblyjs/wasm-parser@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", "@webassemblyjs/utf8": "1.11.6" } }, "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ=="], - - "@webassemblyjs/wast-printer": ["@webassemblyjs/wast-printer@1.12.1", "", { "dependencies": { "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.4", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA=="], "@xmldom/is-dom-node": ["@xmldom/is-dom-node@1.0.1", "", {}, "sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q=="], "@xmldom/xmldom": ["@xmldom/xmldom@0.8.10", "", {}, "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw=="], - "@xtuc/ieee754": ["@xtuc/ieee754@1.2.0", "", {}, "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA=="], - - "@xtuc/long": ["@xtuc/long@4.2.2", "", {}, "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="], - - "abab": ["abab@2.0.6", "", {}, "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="], - "abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="], "abstract-logging": ["abstract-logging@2.0.1", "", {}, "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA=="], @@ -2129,8 +2291,6 @@ "acorn": ["acorn@8.15.0", "", { "bin": "bin/acorn" }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - "acorn-globals": ["acorn-globals@7.0.1", "", { "dependencies": { "acorn": "^8.1.0", "acorn-walk": "^8.0.2" } }, "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q=="], - "acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], @@ -2139,12 +2299,12 @@ "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "ajv": ["ajv@6.12.6", "", { "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" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + "ai-tokenizer": ["ai-tokenizer@1.0.6", "", { "peerDependencies": { "ai": "^5.0.0" }, "optionalPeers": ["ai"] }, "sha512-GaakQFxen0pRH/HIA4v68ZM40llCH27HUYUSBLK+gVuZ57e53pYJe1xFvSTj4sJJjbWU92m1X6NjPWyeWkFDow=="], + + "ajv": ["ajv@6.14.0", "", { "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" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" }, "peerDependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], - "ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="], - "anser": ["anser@2.1.1", "", {}, "sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ=="], "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], @@ -2161,7 +2321,7 @@ "arg": ["arg@4.1.3", "", {}, "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="], - "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], @@ -2223,11 +2383,11 @@ "babel-plugin-jest-hoist": ["babel-plugin-jest-hoist@30.2.0", "", { "dependencies": { "@types/babel__core": "^7.20.5" } }, "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA=="], - "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.12", "", { "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.6.3", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og=="], + "babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="], - "babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.11.1", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3", "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ=="], + "babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="], - "babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.3", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q=="], + "babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="], "babel-plugin-replace-ts-export-assignment": ["babel-plugin-replace-ts-export-assignment@0.0.2", "", {}, "sha512-BiTEG2Ro+O1spuheL5nB289y37FFmz0ISE6GjpNCG2JuA/WNcuEHSYw01+vN8quGf208sID3FnZFDwVyqX18YQ=="], @@ -2253,7 +2413,7 @@ "base64url": ["base64url@3.0.1", "", {}, "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.0", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA=="], "bcryptjs": ["bcryptjs@2.4.3", "", {}, "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="], @@ -2261,9 +2421,11 @@ "binary-extensions": ["binary-extensions@2.2.0", "", {}, "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="], + "bluebird": ["bluebird@3.4.7", "", {}, "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA=="], + "bn.js": ["bn.js@4.12.1", "", {}, "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg=="], - "body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], + "body-parser": ["body-parser@2.2.2", "", { "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.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], @@ -2351,9 +2513,11 @@ "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], - "chokidar": ["chokidar@3.5.3", "", { "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" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw=="], + "chevrotain": ["chevrotain@11.1.2", "", { "dependencies": { "@chevrotain/cst-dts-gen": "11.1.2", "@chevrotain/gast": "11.1.2", "@chevrotain/regexp-to-ast": "11.1.2", "@chevrotain/types": "11.1.2", "@chevrotain/utils": "11.1.2", "lodash-es": "4.17.23" } }, "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg=="], - "chrome-trace-event": ["chrome-trace-event@1.0.3", "", {}, "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="], + "chevrotain-allstar": ["chevrotain-allstar@0.3.1", "", { "dependencies": { "lodash-es": "^4.17.21" }, "peerDependencies": { "chevrotain": "^11.0.0" } }, "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw=="], + + "chokidar": ["chokidar@3.5.3", "", { "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" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw=="], "ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], @@ -2419,6 +2583,8 @@ "concat-with-sourcemaps": ["concat-with-sourcemaps@1.1.0", "", { "dependencies": { "source-map": "^0.6.1" } }, "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg=="], + "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + "connect-redis": ["connect-redis@8.1.0", "", { "peerDependencies": { "express-session": ">=1" } }, "sha512-Km0EYLDlmExF52UCss5gLGTtrukGC57G6WCC2aqEMft5Vr4xNWuM4tL+T97kWrw+vp40SXFteb6Xk/7MxgpwdA=="], "console-browserify": ["console-browserify@1.2.0", "", {}, "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA=="], @@ -2445,13 +2611,13 @@ "copy-to-clipboard": ["copy-to-clipboard@3.3.3", "", { "dependencies": { "toggle-selection": "^1.0.6" } }, "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA=="], - "core-js-compat": ["core-js-compat@3.40.0", "", { "dependencies": { "browserslist": "^4.24.3" } }, "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ=="], + "core-js-compat": ["core-js-compat@3.47.0", "", { "dependencies": { "browserslist": "^4.28.0" } }, "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ=="], - "core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="], + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="], - "cosmiconfig": ["cosmiconfig@8.3.6", "", { "dependencies": { "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0", "path-type": "^4.0.0" }, "peerDependencies": { "typescript": ">=4.9.5" } }, "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA=="], + "cose-base": ["cose-base@1.0.3", "", { "dependencies": { "layout-base": "^1.0.0" } }, "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg=="], "create-ecdh": ["create-ecdh@4.0.4", "", { "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" } }, "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A=="], @@ -2473,13 +2639,13 @@ "crypto-random-string": ["crypto-random-string@2.0.0", "", {}, "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="], - "css-blank-pseudo": ["css-blank-pseudo@5.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-aCU4AZ7uEcVSUzagTlA9pHciz7aWPKA/YzrEkpdSopJ2pvhIxiQ5sYeMz1/KByxlIo4XBdvMNJAVKMg/GRnhfw=="], + "css-blank-pseudo": ["css-blank-pseudo@8.0.1", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-C5B2e5hCM4llrQkUms+KnWEMVW8K1n2XvX9G7ppfMZJQ7KAS/4rNnkP1Cs+HhWriOz1mWWTMFD4j1J7s31Dgug=="], "css-declaration-sorter": ["css-declaration-sorter@6.4.1", "", { "peerDependencies": { "postcss": "^8.0.9" } }, "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g=="], - "css-has-pseudo": ["css-has-pseudo@5.0.2", "", { "dependencies": { "@csstools/selector-specificity": "^2.0.1", "postcss-selector-parser": "^6.0.10", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-q+U+4QdwwB7T9VEW/LyO6CFrLAeLqOykC5mDqJXc7aKZAhDbq7BvGT13VGJe+IwBfdN2o3Xdw2kJ5IxwV1Sc9Q=="], + "css-has-pseudo": ["css-has-pseudo@8.0.0", "", { "dependencies": { "@csstools/selector-specificity": "^6.0.0", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Uz/bsHRbOeir/5Oeuz85tq/yLJLxX+3dpoRdjNTshs6jjqwUg8XaEZGDd0ci3fw7l53Srw0EkJ8mYan0eW5uGQ=="], - "css-prefers-color-scheme": ["css-prefers-color-scheme@8.0.2", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-OvFghizHJ45x7nsJJUSYLyQNTzsCU8yWjxAc/nhPQg1pbs18LMoET8N3kOweFDPy0JV0OSXN2iqRFhPBHYOeMA=="], + "css-prefers-color-scheme": ["css-prefers-color-scheme@11.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-fv0mgtwUhh2m9iio3Kxc2CkrogjIaRdMFaaqyzSFdii17JF4cfPyMNX72B15ZW2Nrr/NZUpxI4dec1VMHYJvdw=="], "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], @@ -2489,7 +2655,7 @@ "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], - "cssdb": ["cssdb@7.10.0", "", {}, "sha512-yGZ5tmA57gWh/uvdQBHs45wwFY0IBh3ypABk5sEubPBPSzXzkNgsWReqx7gdx6uhC+QoFBe+V8JwBB9/hQ6cIA=="], + "cssdb": ["cssdb@8.8.0", "", {}, "sha512-QbLeyz2Bgso1iRlh7IpWk6OKa3lLNGXsujVjDMPl9rOZpxKeiG69icLpbLCFxeURwmcdIfZqQyhlooKJYM4f8Q=="], "cssesc": ["cssesc@3.0.0", "", { "bin": "bin/cssesc" }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], @@ -2503,14 +2669,84 @@ "csso": ["csso@4.2.0", "", { "dependencies": { "css-tree": "^1.1.2" } }, "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA=="], - "cssom": ["cssom@0.5.0", "", {}, "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw=="], - "cssstyle": ["cssstyle@4.6.0", "", { "dependencies": { "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" } }, "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg=="], "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "cytoscape": ["cytoscape@3.33.1", "", {}, "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ=="], + + "cytoscape-cose-bilkent": ["cytoscape-cose-bilkent@4.1.0", "", { "dependencies": { "cose-base": "^1.0.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ=="], + + "cytoscape-fcose": ["cytoscape-fcose@2.2.0", "", { "dependencies": { "cose-base": "^2.2.0" }, "peerDependencies": { "cytoscape": "^3.2.0" } }, "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ=="], + "d": ["d@1.0.2", "", { "dependencies": { "es5-ext": "^0.10.64", "type": "^2.7.2" } }, "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw=="], + "d3": ["d3@7.9.0", "", { "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" } }, "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA=="], + + "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], + + "d3-axis": ["d3-axis@3.0.0", "", {}, "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw=="], + + "d3-brush": ["d3-brush@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "3", "d3-transition": "3" } }, "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ=="], + + "d3-chord": ["d3-chord@3.0.1", "", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="], + + "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], + + "d3-contour": ["d3-contour@4.0.2", "", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="], + + "d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="], + + "d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="], + + "d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="], + + "d3-dsv": ["d3-dsv@3.0.1", "", { "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" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="], + + "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], + + "d3-fetch": ["d3-fetch@3.0.1", "", { "dependencies": { "d3-dsv": "1 - 3" } }, "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw=="], + + "d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="], + + "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], + + "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="], + + "d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="], + + "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], + + "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], + + "d3-polygon": ["d3-polygon@3.0.1", "", {}, "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg=="], + + "d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="], + + "d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="], + + "d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="], + + "d3-scale": ["d3-scale@4.0.2", "", { "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" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], + + "d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="], + + "d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="], + + "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], + + "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], + + "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], + + "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], + + "d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="], + + "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="], + + "dagre-d3-es": ["dagre-d3-es@7.0.14", "", { "dependencies": { "d3": "^7.9.0", "lodash-es": "^4.17.21" } }, "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg=="], + "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="], "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], @@ -2525,9 +2761,9 @@ "date-fns": ["date-fns@3.3.1", "", {}, "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw=="], - "dayjs": ["dayjs@1.11.13", "", {}, "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="], + "dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="], - "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], "decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="], @@ -2553,6 +2789,8 @@ "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + "delaunator": ["delaunator@5.0.1", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="], + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], "denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="], @@ -2577,12 +2815,14 @@ "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], - "diff": ["diff@7.0.0", "", {}, "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw=="], + "diff": ["diff@4.0.2", "", {}, "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="], "diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="], "diffie-hellman": ["diffie-hellman@5.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", "randombytes": "^2.0.0" } }, "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg=="], + "dingbat-to-unicode": ["dingbat-to-unicode@1.0.1", "", {}, "sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w=="], + "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], "dnd-core": ["dnd-core@16.0.1", "", { "dependencies": { "@react-dnd/asap": "^5.0.1", "@react-dnd/invariant": "^4.0.1", "redux": "^4.2.0" } }, "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng=="], @@ -2599,11 +2839,9 @@ "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], - "domexception": ["domexception@4.0.0", "", { "dependencies": { "webidl-conversions": "^7.0.0" } }, "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw=="], - "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], - "dompurify": ["dompurify@3.3.0", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ=="], + "dompurify": ["dompurify@3.3.2", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ=="], "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], @@ -2611,6 +2849,8 @@ "downloadjs": ["downloadjs@1.4.7", "", {}, "sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q=="], + "duck": ["duck@0.1.12", "", { "dependencies": { "underscore": "^1.13.1" } }, "sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg=="], + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], @@ -2639,7 +2879,7 @@ "enhanced-resolve": ["enhanced-resolve@5.17.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg=="], - "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], @@ -2655,8 +2895,6 @@ "es-iterator-helpers": ["es-iterator-helpers@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.4", "safe-array-concat": "^1.1.3" } }, "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w=="], - "es-module-lexer": ["es-module-lexer@1.6.0", "", {}, "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ=="], - "es-object-atoms": ["es-object-atoms@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw=="], "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], @@ -2671,7 +2909,7 @@ "es6-symbol": ["es6-symbol@3.1.4", "", { "dependencies": { "d": "^1.0.2", "ext": "^1.7.0" } }, "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg=="], - "esbuild": ["esbuild@0.25.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.1", "@esbuild/android-arm": "0.25.1", "@esbuild/android-arm64": "0.25.1", "@esbuild/android-x64": "0.25.1", "@esbuild/darwin-arm64": "0.25.1", "@esbuild/darwin-x64": "0.25.1", "@esbuild/freebsd-arm64": "0.25.1", "@esbuild/freebsd-x64": "0.25.1", "@esbuild/linux-arm": "0.25.1", "@esbuild/linux-arm64": "0.25.1", "@esbuild/linux-ia32": "0.25.1", "@esbuild/linux-loong64": "0.25.1", "@esbuild/linux-mips64el": "0.25.1", "@esbuild/linux-ppc64": "0.25.1", "@esbuild/linux-riscv64": "0.25.1", "@esbuild/linux-s390x": "0.25.1", "@esbuild/linux-x64": "0.25.1", "@esbuild/netbsd-arm64": "0.25.1", "@esbuild/netbsd-x64": "0.25.1", "@esbuild/openbsd-arm64": "0.25.1", "@esbuild/openbsd-x64": "0.25.1", "@esbuild/sunos-x64": "0.25.1", "@esbuild/win32-arm64": "0.25.1", "@esbuild/win32-ia32": "0.25.1", "@esbuild/win32-x64": "0.25.1" }, "bin": "bin/esbuild" }, "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ=="], + "esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -2683,8 +2921,6 @@ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], - "escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "escodegen": "bin/escodegen.js", "esgenerate": "bin/esgenerate.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="], - "eslint": ["eslint@9.39.1", "", { "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" }, "peerDependencies": { "jiti": "*" }, "bin": "bin/eslint.js" }, "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g=="], "eslint-config-prettier": ["eslint-config-prettier@10.0.1", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": "build/bin/cli.js" }, "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw=="], @@ -2755,11 +2991,11 @@ "export-from-json": ["export-from-json@1.7.4", "", {}, "sha512-FjmpluvZS2PTYyhkoMfQoyEJMfe2bfAyNpa5Apa6C9n7SWUWyJkG/VFnzERuj3q9Jjo3iwBjwVsDQ7Z7sczthA=="], - "express": ["express@5.1.0", "", { "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" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.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" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], "express-mongo-sanitize": ["express-mongo-sanitize@2.2.0", "", {}, "sha512-PZBs5nwhD6ek9ZuP+W2xmpvcrHwXZxD5GdieX2dsjPbAbH4azOkrHbycBud2QRU+YQF1CT+pki/lZGedHgo/dQ=="], - "express-rate-limit": ["express-rate-limit@8.2.1", "", { "dependencies": { "ip-address": "10.0.1" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g=="], + "express-rate-limit": ["express-rate-limit@8.3.1", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw=="], "express-session": ["express-session@1.18.2", "", { "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.7", "debug": "2.6.9", "depd": "~2.0.0", "on-headers": "~1.1.0", "parseurl": "~1.3.3", "safe-buffer": "5.2.1", "uid-safe": "~2.1.5" } }, "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A=="], @@ -2787,7 +3023,7 @@ "fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="], - "fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="], + "fast-xml-parser": ["fast-xml-parser@5.3.8", "", { "dependencies": { "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-53jIF4N6u/pxvaL1eb/hEZts/cFLWZ92eCfLrNyCI0k38lettCG/Bs40W9pPwoPXyHQlKu2OUbQtiEIZK/J6Vw=="], "fastq": ["fastq@1.17.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w=="], @@ -2807,7 +3043,7 @@ "file-stream-rotator": ["file-stream-rotator@0.6.1", "", { "dependencies": { "moment": "^2.29.1" } }, "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ=="], - "file-type": ["file-type@18.7.0", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.2", "strtok3": "^7.0.0", "token-types": "^5.0.1" } }, "sha512-ihHtXRzXEziMrQ56VSgU7wkxh55iNchFkosu7Y9/S+tXHdKyrGjVK0ujbqNnsxzea+78MaLhN6PGmfYSAv1ACw=="], + "file-type": ["file-type@21.3.3", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-pNwbwz8c3aZ+GvbJnIsCnDjKvgCZLHxkFWLEFxU3RMa+Ey++ZSEfisvsWQMcdys6PpxQjWUOIDi1fifXsW3YRg=="], "filelist": ["filelist@1.0.4", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="], @@ -2849,7 +3085,7 @@ "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], + "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], "framer-motion": ["framer-motion@12.23.9", "", { "dependencies": { "motion-dom": "^12.23.9", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid"] }, "sha512-TqEHXj8LWfQSKqfdr5Y4mYltYLw96deu6/K9kGDd+ysqRJPNwF9nb5mZcrLmybHbU7gcJ+HQar41U3UTGanbbQ=="], @@ -2867,7 +3103,7 @@ "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="], - "gaxios": ["gaxios@5.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^5.0.0", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA=="], + "gaxios": ["gaxios@6.2.0", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ=="], "gcp-metadata": ["gcp-metadata@5.3.0", "", { "dependencies": { "gaxios": "^5.0.0", "json-bigint": "^1.0.0" } }, "sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w=="], @@ -2897,12 +3133,10 @@ "get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="], - "glob": ["glob@13.0.0", "", { "dependencies": { "minimatch": "^10.1.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA=="], + "glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], - "globals": ["globals@15.14.0", "", {}, "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig=="], "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], @@ -2911,8 +3145,6 @@ "google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="], - "googleapis-common": ["googleapis-common@7.0.1", "", { "dependencies": { "extend": "^3.0.2", "gaxios": "^6.0.3", "google-auth-library": "^9.0.0", "qs": "^6.7.0", "url-template": "^2.0.8", "uuid": "^9.0.0" } }, "sha512-mgt5zsd7zj5t5QXvDanjWguMdHAcJmmDrF9RkInCecNsyV7S7YtGqm5v2IWONNID88osb7zmx5FtrAP12JfD0w=="], - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], @@ -2921,10 +3153,14 @@ "gtoken": ["gtoken@7.1.0", "", { "dependencies": { "gaxios": "^6.0.0", "jws": "^4.0.0" } }, "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw=="], + "hachure-fill": ["hachure-fill@0.5.2", "", {}, "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg=="], + "hamt_plus": ["hamt_plus@1.0.2", "", {}, "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="], "handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": "bin/handlebars" }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="], + "happy-dom": ["happy-dom@20.8.3", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-lMHQRRwIPyJ70HV0kkFT7jH/gXzSI7yDkQFe07E2flwmNDFoWUTRMKpW2sglsnpeA7b6S2TJPp98EbQxai8eaQ=="], + "harmony-reflect": ["harmony-reflect@1.6.2", "", {}, "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g=="], "has-bigints": ["has-bigints@1.0.2", "", {}, "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ=="], @@ -2973,7 +3209,7 @@ "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="], - "hono": ["hono@4.11.1", "", {}, "sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg=="], + "hono": ["hono@4.12.5", "", {}, "sha512-3qq+FUBtlTHhtYxbxheZgY8NIFnkkC/MR8u5TTsr7YZ3wixryQ3cCwn3iZbg8p8B88iDBBAYSfZDS75t8MN7Vg=="], "hookified": ["hookified@1.12.1", "", {}, "sha512-xnKGl+iMIlhrZmGHB729MqlmPoWBznctSQTYCpFKqNsCgimJQmithcW0xSQMMFzYnV2iKUh25alswn6epgxS0Q=="], @@ -3025,6 +3261,8 @@ "ignore-by-default": ["ignore-by-default@1.0.1", "", {}, "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="], + "immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="], + "import-cwd": ["import-cwd@3.0.0", "", { "dependencies": { "import-from": "^3.0.0" } }, "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg=="], "import-fresh": ["import-fresh@3.3.0", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw=="], @@ -3049,11 +3287,13 @@ "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], + "intersection-observer": ["intersection-observer@0.10.0", "", {}, "sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ=="], "ioredis": ["ioredis@5.3.2", "", { "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA=="], - "ip-address": ["ip-address@10.0.1", "", {}, "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA=="], + "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], @@ -3215,7 +3455,7 @@ "jest-message-util": ["jest-message-util@30.2.0", "", { "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" } }, "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw=="], - "jest-mock": ["jest-mock@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" } }, "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw=="], + "jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="], "jest-pnp-resolver": ["jest-pnp-resolver@1.2.3", "", { "peerDependencies": { "jest-resolve": "*" } }, "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w=="], @@ -3231,7 +3471,7 @@ "jest-snapshot": ["jest-snapshot@30.2.0", "", { "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" } }, "sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA=="], - "jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + "jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], "jest-validate": ["jest-validate@30.2.0", "", { "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" } }, "sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw=="], @@ -3283,6 +3523,8 @@ "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="], + "jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="], + "jwa": ["jwa@2.0.0", "", { "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA=="], "jwks-rsa": ["jwks-rsa@3.2.0", "", { "dependencies": { "@types/express": "^4.17.20", "@types/jsonwebtoken": "^9.0.4", "debug": "^4.3.4", "jose": "^4.15.4", "limiter": "^1.1.5", "lru-memoizer": "^2.2.0" } }, "sha512-PwchfHcQK/5PSydeKCs1ylNym0w/SSv8a62DgHJ//7x2ZclCoinlsjAfDxAAbpoTPybOum/Jgy+vkvMmKz89Ww=="], @@ -3297,16 +3539,22 @@ "keyv-file": ["keyv-file@5.2.0", "", { "dependencies": { "@keyv/serialize": "^1.0.1", "tslib": "^1.14.1" } }, "sha512-5JEBqQiDzjGCQHtf7KLReJdHKchaJyUZW+9TvBu+4dc+uuTqUG9KcdA3ICMXlwky3qjKc0ecNCNefbgjyDtlAg=="], + "khroma": ["khroma@2.1.0", "", {}, "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="], + "klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="], "kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="], - "langsmith": ["langsmith@0.3.67", "", { "dependencies": { "@types/uuid": "^10.0.0", "chalk": "^4.1.2", "console-table-printer": "^2.12.1", "p-queue": "^6.6.2", "p-retry": "4", "semver": "^7.6.3", "uuid": "^10.0.0" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*" } }, "sha512-l4y3RmJ9yWF5a29fLg3eWZQxn6Q6dxTOgLGgQHzPGZHF3NUynn+A+airYIe/Yt4rwjGbuVrABAPsXBkVu/Hi7g=="], + "langium": ["langium@4.2.1", "", { "dependencies": { "chevrotain": "~11.1.1", "chevrotain-allstar": "~0.3.1", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", "vscode-uri": "~3.1.0" } }, "sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ=="], + + "langsmith": ["langsmith@0.4.12", "", { "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": "*" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/sdk-trace-base", "openai"] }, "sha512-YWt0jcGvKqjUgIvd78rd4QcdMss0lUkeUaqp0UpVRq7H2yNDx8H5jOUO/laWUmaPtWGgcip0qturykXe1g9Gqw=="], "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="], "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="], + "layout-base": ["layout-base@1.0.2", "", {}, "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg=="], + "ldap-filter": ["ldap-filter@0.3.3", "", { "dependencies": { "assert-plus": "^1.0.0" } }, "sha512-/tFkx5WIn4HuO+6w9lsfxq4FN3O+fDZeO9Mek8dCD8rTUpqzRa766BOBO7BcGkn3X86m5+cBm1/2S/Shzz7gMg=="], "ldapauth-fork": ["ldapauth-fork@5.0.5", "", { "dependencies": { "@types/ldapjs": "^2.2.2", "bcryptjs": "^2.4.0", "ldapjs": "^2.2.1", "lru-cache": "^7.10.1" } }, "sha512-LWUk76+V4AOZbny/3HIPQtGPWZyA3SW2tRhsWIBi9imP22WJktKLHV1ofd8Jo/wY7Ve6vAT7FCI5mEn3blZTjw=="], @@ -3319,6 +3567,8 @@ "librechat-data-provider": ["librechat-data-provider@workspace:packages/data-provider"], + "lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="], + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], "limiter": ["limiter@1.1.5", "", {}, "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="], @@ -3329,13 +3579,13 @@ "listr2": ["listr2@8.2.5", "", { "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ=="], - "loader-runner": ["loader-runner@4.3.0", "", {}, "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg=="], - "loader-utils": ["loader-utils@3.3.1", "", {}, "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg=="], "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], - "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="], + + "lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="], "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], @@ -3367,8 +3617,6 @@ "lodash.sortby": ["lodash.sortby@4.7.0", "", {}, "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA=="], - "lodash.throttle": ["lodash.throttle@4.1.1", "", {}, "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="], - "lodash.uniq": ["lodash.uniq@4.5.0", "", {}, "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="], "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], @@ -3381,6 +3629,8 @@ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": "cli.js" }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "lop": ["lop@0.4.2", "", { "dependencies": { "duck": "^0.1.12", "option": "~0.2.1", "underscore": "^1.13.1" } }, "sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw=="], + "lowlight": ["lowlight@2.9.0", "", { "dependencies": { "@types/hast": "^2.0.0", "fault": "^2.0.0", "highlight.js": "~11.8.0" } }, "sha512-OpcaUTCLmHuVuBcyNckKfH5B0oA4JUavb/M/8n9iAvanJYNQkrVm4pvyX0SUaqkBG4dnWHKt7p50B3ngAG2Rfw=="], "lru-cache": ["lru-cache@4.1.5", "", { "dependencies": { "pseudomap": "^1.0.2", "yallist": "^2.1.2" } }, "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g=="], @@ -3399,8 +3649,12 @@ "makeerror": ["makeerror@1.0.12", "", { "dependencies": { "tmpl": "1.0.5" } }, "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg=="], + "mammoth": ["mammoth@1.11.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.6", "argparse": "~1.0.3", "base64-js": "^1.5.1", "bluebird": "~3.4.0", "dingbat-to-unicode": "^1.0.1", "jszip": "^3.7.1", "lop": "^0.4.2", "path-is-absolute": "^1.0.0", "underscore": "^1.13.1", "xmlbuilder": "^10.0.0" }, "bin": { "mammoth": "bin/mammoth" } }, "sha512-BcEqqY/BOwIcI1iR5tqyVlqc3KIaMRa4egSoK83YAVrBf6+yqdAAbtUcFDCWX8Zef8/fgNZ6rl4VUv+vVX8ddQ=="], + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + "marked": ["marked@14.0.0", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ=="], + "match-sorter": ["match-sorter@8.1.0", "", { "dependencies": { "@babel/runtime": "^7.23.8", "remove-accents": "0.5.0" } }, "sha512-0HX3BHPixkbECX+Vt7nS1vJ6P2twPgGTU3PMXjWrl1eyVCL24tFHeyYN1FN5RKLzve0TyzNI9qntqQGbebnfPQ=="], "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], @@ -3459,6 +3713,8 @@ "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], + "mermaid": ["mermaid@11.13.0", "", { "dependencies": { "@braintree/sanitize-url": "^7.1.1", "@iconify/utils": "^3.0.2", "@mermaid-js/parser": "^1.0.1", "@types/d3": "^7.4.3", "@upsetjs/venn.js": "^2.0.0", "cytoscape": "^3.33.1", "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.14", "dayjs": "^1.11.19", "dompurify": "^3.3.1", "katex": "^0.16.25", "khroma": "^2.1.0", "lodash-es": "^4.17.23", "marked": "^16.3.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", "uuid": "^11.1.0" } }, "sha512-fEnci+Immw6lKMFI8sqzjlATTyjLkRa6axrEgLV2yHTfv8r+h1wjFbV6xeRtd4rUV1cS4EpR9rwp3Rci7TRWDw=="], + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], "micromark": ["micromark@4.0.0", "", { "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" } }, "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ=="], @@ -3543,20 +3799,24 @@ "minimalistic-crypto-utils": ["minimalistic-crypto-utils@1.0.1", "", {}, "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="], - "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], "mkdirp": ["mkdirp@1.0.4", "", { "bin": "bin/cmd.js" }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], + "mlly": ["mlly@1.8.1", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ=="], + "module-alias": ["module-alias@2.2.3", "", {}, "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q=="], "module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="], "moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="], + "monaco-editor": ["monaco-editor@0.55.1", "", { "dependencies": { "dompurify": "3.2.7", "marked": "14.0.0" } }, "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A=="], + "mongodb": ["mongodb@6.14.2", "", { "dependencies": { "@mongodb-js/saslprep": "^1.1.9", "bson": "^6.10.3", "mongodb-connection-string-url": "^3.0.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.188.0", "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", "gcp-metadata": "^5.2.0", "kerberos": "^2.0.1", "mongodb-client-encryption": ">=6.0.0 <7", "snappy": "^7.2.2", "socks": "^2.7.1" }, "optionalPeers": ["@mongodb-js/zstd", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-kMEHNo0F3P6QKDq17zcDuPeaywK/YaJVCEQRzPF3TOM/Bl9MFg64YE5Tu7ifj37qZJMhwU1tl2Ioivws5gRG5Q=="], "mongodb-connection-string-url": ["mongodb-connection-string-url@3.0.2", "", { "dependencies": { "@types/whatwg-url": "^11.0.2", "whatwg-url": "^14.1.0 || ^13.0.0" } }, "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA=="], @@ -3579,13 +3839,13 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "multer": ["multer@2.0.2", "", { "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" } }, "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw=="], + "multer": ["multer@2.1.1", "", { "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "concat-stream": "^2.0.0", "type-is": "^1.6.18" } }, "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A=="], "mustache": ["mustache@4.2.0", "", { "bin": "bin/mustache" }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="], "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], - "nanoid": ["nanoid@3.3.8", "", { "bin": "bin/nanoid.cjs" }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": "lib/cli.js" }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], @@ -3605,6 +3865,8 @@ "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], + "node-readable-to-web-readable-stream": ["node-readable-to-web-readable-stream@0.4.2", "", {}, "sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ=="], + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], "node-stdlib-browser": ["node-stdlib-browser@1.3.1", "", { "dependencies": { "assert": "^2.0.0", "browser-resolve": "^2.0.0", "browserify-zlib": "^0.2.0", "buffer": "^5.7.1", "console-browserify": "^1.1.0", "constants-browserify": "^1.0.0", "create-require": "^1.1.1", "crypto-browserify": "^3.12.1", "domain-browser": "4.22.0", "events": "^3.0.0", "https-browserify": "^1.0.0", "isomorphic-timers-promises": "^1.0.1", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", "pkg-dir": "^5.0.0", "process": "^0.11.10", "punycode": "^1.4.1", "querystring-es3": "^0.2.1", "readable-stream": "^3.6.0", "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", "string_decoder": "^1.0.0", "timers-browserify": "^2.0.4", "tty-browserify": "0.0.1", "url": "^0.11.4", "util": "^0.12.4", "vm-browserify": "^1.0.1" } }, "sha512-X75ZN8DCLftGM5iKwoYLA3rjnrAEs97MkzvSd4q2746Tgpg8b8XWiBGiBG4ZpgcAqBgtgPHTiAc8ZMCvZuikDw=="], @@ -3651,6 +3913,8 @@ "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + "okapibm25": ["okapibm25@1.4.1", "", {}, "sha512-UHmeH4MAtZXGFVncwbY7pfFvDVNxpsyM3W66aGPU0SHj1+ld59ty+9lJ0ifcrcnPUl1XdYoDgb06ObyCnpTs3g=="], + "ollama": ["ollama@0.5.18", "", { "dependencies": { "whatwg-fetch": "^3.6.20" } }, "sha512-lTFqTf9bo7Cd3hpF6CviBe/DEhewjoZYd9N/uCe7O20qYTvGqrNOFOBDj3lbZgFWHUgDv5EeyusYxsZSLS8nvg=="], "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], @@ -3671,6 +3935,8 @@ "openid-client": ["openid-client@6.5.0", "", { "dependencies": { "jose": "^6.0.10", "oauth4webapi": "^3.5.1" } }, "sha512-fAfYaTnOYE2kQCqEJGX9KDObW2aw7IQy4jWpU/+3D3WoCFLbix5Hg6qIPQ6Js9r7f8jDUmsnnguRNCSw4wU/IQ=="], + "option": ["option@0.2.4", "", {}, "sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A=="], + "optionator": ["optionator@0.9.3", "", { "dependencies": { "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0" } }, "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg=="], "os-browserify": ["os-browserify@0.3.0", "", {}, "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A=="], @@ -3695,6 +3961,8 @@ "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], + "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], @@ -3737,6 +4005,8 @@ "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + "path-data-parser": ["path-data-parser@0.1.0", "", {}, "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w=="], + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], @@ -3745,17 +4015,17 @@ "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], - "path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], + "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], "path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], - "path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "pause": ["pause@0.0.1", "", {}, "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="], "pbkdf2": ["pbkdf2@3.1.3", "", { "dependencies": { "create-hash": "~1.1.3", "create-hmac": "^1.1.7", "ripemd160": "=2.0.1", "safe-buffer": "^5.2.1", "sha.js": "^2.4.11", "to-buffer": "^1.2.0" } }, "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA=="], - "peek-readable": ["peek-readable@5.0.0", "", {}, "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A=="], + "pdfjs-dist": ["pdfjs-dist@5.5.207", "", { "optionalDependencies": { "@napi-rs/canvas": "^0.1.95", "node-readable-to-web-readable-stream": "^0.4.2" } }, "sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw=="], "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], @@ -3773,37 +4043,43 @@ "pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="], + "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + "playwright": ["playwright@1.56.1", "", { "dependencies": { "playwright-core": "1.56.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": "cli.js" }, "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw=="], "playwright-core": ["playwright-core@1.56.1", "", { "bin": "cli.js" }, "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ=="], + "points-on-curve": ["points-on-curve@0.2.0", "", {}, "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A=="], + + "points-on-path": ["points-on-path@0.2.1", "", { "dependencies": { "path-data-parser": "0.1.0", "points-on-curve": "0.2.0" } }, "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g=="], + "possible-typed-array-names": ["possible-typed-array-names@1.0.0", "", {}, "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q=="], "postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="], - "postcss-attribute-case-insensitive": ["postcss-attribute-case-insensitive@6.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-IRuCwwAAQbgaLhxQdQcIIK0dCVXg3XDUnzgKD8iwdiYdwU4rMWRWyl/W9/0nA4ihVpq5pyALiHB2veBJ0292pw=="], + "postcss-attribute-case-insensitive": ["postcss-attribute-case-insensitive@8.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-fovIPEV35c2JzVXdmP+sp2xirbBMt54J+upU8u6TSj410kUU5+axgEzvBBSAX8KCybze8CFCelzFAw/FfWg2TA=="], "postcss-calc": ["postcss-calc@8.2.4", "", { "dependencies": { "postcss-selector-parser": "^6.0.9", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.2" } }, "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q=="], "postcss-clamp": ["postcss-clamp@4.1.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4.6" } }, "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow=="], - "postcss-color-functional-notation": ["postcss-color-functional-notation@5.1.0", "", { "dependencies": { "@csstools/postcss-progressive-custom-properties": "^2.3.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-w2R4py6zrVE1U7FwNaAc76tNQlG9GLkrBbcFw+VhUjyDDiV28vfZG+l4LyPmpoQpeSJVtu8VgNjE8Jv5SpC7dQ=="], + "postcss-color-functional-notation": ["postcss-color-functional-notation@8.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-tbmkk6teYpJzFcGwPIhN1gkvxqGHvNx2PMb8Y3S5Ktyn7xOlvD98XzQ99MFY5mAyvXWclDG+BgoJKYJXFJOp5Q=="], - "postcss-color-hex-alpha": ["postcss-color-hex-alpha@9.0.3", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-7sEHU4tAS6htlxun8AB9LDrCXoljxaC34tFVRlYKcvO+18r5fvGiXgv5bQzN40+4gXLCyWSMRK5FK31244WcCA=="], + "postcss-color-hex-alpha": ["postcss-color-hex-alpha@11.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-NCGa6vjIyrjosz9GqRxVKbONBklz5TeipYqTJp3IqbnBWlBq5e5EMtG6MaX4vqk9LzocPfMQkuRK9tfk+OQuKg=="], - "postcss-color-rebeccapurple": ["postcss-color-rebeccapurple@8.0.2", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-xWf/JmAxVoB5bltHpXk+uGRoGFwu4WDAR7210el+iyvTdqiKpDhtcT8N3edXMoVJY0WHFMrKMUieql/wRNiXkw=="], + "postcss-color-rebeccapurple": ["postcss-color-rebeccapurple@11.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-g9561mx7cbdqx7XeO/L+lJzVlzu7bICyXr72efBVKZGxIhvBBJf9fGXn3Cb6U4Bwh3LbzQO2e9NWBLVYdX5Eag=="], "postcss-colormin": ["postcss-colormin@5.3.1", "", { "dependencies": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", "colord": "^2.9.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ=="], "postcss-convert-values": ["postcss-convert-values@5.1.3", "", { "dependencies": { "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA=="], - "postcss-custom-media": ["postcss-custom-media@9.1.5", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^1.0.2", "@csstools/css-parser-algorithms": "^2.2.0", "@csstools/css-tokenizer": "^2.1.1", "@csstools/media-query-list-parser": "^2.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-GStyWMz7Qbo/Gtw1xVspzVSX8eipgNg4lpsO3CAeY4/A1mzok+RV6MCv3fg62trWijh/lYEj6vps4o8JcBBpDA=="], + "postcss-custom-media": ["postcss-custom-media@12.0.1", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^3.0.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-66syE14+VeqkUf0rRX0bvbTCbNRJF132jD+ceo8th1dap2YJEAqpdh5uG98CE3IbgHT7m9XM0GIlOazNWqQdeA=="], - "postcss-custom-properties": ["postcss-custom-properties@13.3.4", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^1.0.7", "@csstools/css-parser-algorithms": "^2.5.0", "@csstools/css-tokenizer": "^2.2.3", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-9YN0gg9sG3OH+Z9xBrp2PWRb+O4msw+5Sbp3ZgqrblrwKspXVQe5zr5sVqi43gJGwW/Rv1A483PRQUzQOEewvA=="], + "postcss-custom-properties": ["postcss-custom-properties@15.0.1", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^3.0.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-cuyq8sd8dLY0GLbelz1KB8IMIoDECo6RVXMeHeXY2Uw3Q05k/d1GVITdaKLsheqrHbnxlwxzSRZQQ5u+rNtbMg=="], - "postcss-custom-selectors": ["postcss-custom-selectors@7.1.6", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^1.0.5", "@csstools/css-parser-algorithms": "^2.3.2", "@csstools/css-tokenizer": "^2.2.1", "postcss-selector-parser": "^6.0.13" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-svsjWRaxqL3vAzv71dV0/65P24/FB8TbPX+lWyyf9SZ7aZm4S4NhCn7N3Bg+Z5sZunG3FS8xQ80LrCU9hb37cw=="], + "postcss-custom-selectors": ["postcss-custom-selectors@9.0.1", "", { "dependencies": { "@csstools/cascade-layer-name-parser": "^3.0.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-2XBELy4DmdVKimChfaZ2id9u9CSGYQhiJ53SvlfBvMTzLMW2VxuMb9rHsMSQw9kRq/zSbhT5x13EaK8JSmK8KQ=="], - "postcss-dir-pseudo-class": ["postcss-dir-pseudo-class@7.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-cMnslilYxBf9k3qejnovrUONZx1rXeUZJw06fgIUBzABJe3D2LiLL5WAER7Imt3nrkaIgG05XZBztueLEf5P8w=="], + "postcss-dir-pseudo-class": ["postcss-dir-pseudo-class@10.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-DmtIzULpyC8XaH4b5AaUgt4Jic4QmrECqidNCdR7u7naQFdnxX80YI06u238a+ZVRXwURDxVzy0s/UQnWmpVeg=="], "postcss-discard-comments": ["postcss-discard-comments@5.1.2", "", { "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ=="], @@ -3813,31 +4089,27 @@ "postcss-discard-overridden": ["postcss-discard-overridden@5.1.0", "", { "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw=="], - "postcss-double-position-gradients": ["postcss-double-position-gradients@4.0.4", "", { "dependencies": { "@csstools/postcss-progressive-custom-properties": "^2.3.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-nUAbUXURemLXIrl4Xoia2tiu5z/n8sY+BVDZApoeT9BlpByyrp02P/lFCRrRvZ/zrGRE+MOGLhk8o7VcMCtPtQ=="], + "postcss-double-position-gradients": ["postcss-double-position-gradients@7.0.0", "", { "dependencies": { "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Msr/dxj8Os7KLJE5Hdhvprwm3K5Zrh1KTY0eFN3ngPKNkej/Usy4BM9JQmqE6CLAkDpHoQVsi4snbL72CPt6qg=="], - "postcss-focus-visible": ["postcss-focus-visible@8.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-f/Vd+EC/GaKElknU59esVcRYr/Y3t1ZAQyL4u2xSOgkDy4bMCmG7VP5cGvj3+BTLNE9ETfEuz2nnt4qkZwTTeA=="], + "postcss-focus-visible": ["postcss-focus-visible@11.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-VG1a9kBKizUBWS66t5xyB4uLONBnvZLCmZXxT40FALu8EF0QgVZBYy5ApC0KhmpHsv+pvHMJHB3agKHwmocWjw=="], - "postcss-focus-within": ["postcss-focus-within@7.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-AHAJ89UQBcqBvFgQJE9XasGuwMNkKsGj4D/f9Uk60jFmEBHpAL14DrnSk3Rj+SwZTr/WUG+mh+Rvf8fid/346w=="], + "postcss-focus-within": ["postcss-focus-within@10.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-dvql0fzUTG+gcJYp+KTbag5vAjuo94LDYZHkqDV1rnf5gPGer1v/SrmIZBdvKU8moep3HbcbujqGjzSb3DL53Q=="], "postcss-font-variant": ["postcss-font-variant@5.0.0", "", { "peerDependencies": { "postcss": "^8.1.0" } }, "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA=="], - "postcss-gap-properties": ["postcss-gap-properties@4.0.1", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-V5OuQGw4lBumPlwHWk/PRfMKjaq/LTGR4WDTemIMCaMevArVfCCA9wBJiL1VjDAd+rzuCIlkRoRvDsSiAaZ4Fg=="], + "postcss-gap-properties": ["postcss-gap-properties@7.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-PSDF2QoZMRUbsINvXObQgxx4HExRP85QTT8qS/YN9fBsCPWCqUuwqAD6E6PNp0BqL/jU1eyWUBORaOK/J/9LDA=="], - "postcss-image-set-function": ["postcss-image-set-function@5.0.2", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Sszjwo0ubETX0Fi5MvpYzsONwrsjeabjMoc5YqHvURFItXgIu3HdCjcVuVKGMPGzKRhgaknmdM5uVWInWPJmeg=="], + "postcss-image-set-function": ["postcss-image-set-function@8.0.0", "", { "dependencies": { "@csstools/utilities": "^3.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-rEGNkOkNusf4+IuMmfEoIdLuVmvbExGbmG+MIsyV6jR5UaWSoyPcAYHV/PxzVDCmudyF+2Nh/o6Ub2saqUdnuA=="], "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], - "postcss-initial": ["postcss-initial@4.0.1", "", { "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ=="], - "postcss-js": ["postcss-js@4.0.1", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw=="], - "postcss-lab-function": ["postcss-lab-function@5.2.3", "", { "dependencies": { "@csstools/css-color-parser": "^1.2.0", "@csstools/css-parser-algorithms": "^2.1.1", "@csstools/css-tokenizer": "^2.1.1", "@csstools/postcss-progressive-custom-properties": "^2.3.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-fi32AYKzji5/rvgxo5zXHFvAYBw0u0OzELbeCNjEZVLUir18Oj+9RmNphtM8QdLUaUnrfx8zy8vVYLmFLkdmrQ=="], + "postcss-lab-function": ["postcss-lab-function@8.0.2", "", { "dependencies": { "@csstools/css-color-parser": "^4.0.2", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/utilities": "^3.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-1ZIAh8ODhZdnAb09Aq2BTenePKS1G/kUR0FwvzkQDfFtSOV64Ycv27YvV11fDycEvhIcEmgYkLABXKRiWcXRuA=="], "postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" } }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="], - "postcss-loader": ["postcss-loader@7.3.4", "", { "dependencies": { "cosmiconfig": "^8.3.5", "jiti": "^1.20.0", "semver": "^7.5.4" }, "peerDependencies": { "postcss": "^7.0.0 || ^8.0.1", "webpack": "^5.0.0" } }, "sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A=="], - - "postcss-logical": ["postcss-logical@6.2.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-aqlfKGaY0nnbgI9jwUikp4gJKBqcH5noU/EdnIVceghaaDPYhZuyJVxlvWNy55tlTG5tunRKCTAX9yljLiFgmw=="], + "postcss-logical": ["postcss-logical@9.0.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-A4LNd9dk3q/juEUA9Gd8ALhBO3TeOeYurnyHLlf2aAToD94VHR8c5Uv7KNmf8YVRhTxvWsyug4c5fKtARzyIRQ=="], "postcss-merge-longhand": ["postcss-merge-longhand@5.1.7", "", { "dependencies": { "postcss-value-parser": "^4.2.0", "stylehacks": "^5.1.1" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ=="], @@ -3863,7 +4135,7 @@ "postcss-nested": ["postcss-nested@6.0.1", "", { "dependencies": { "postcss-selector-parser": "^6.0.11" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ=="], - "postcss-nesting": ["postcss-nesting@11.3.0", "", { "dependencies": { "@csstools/selector-specificity": "^2.0.0", "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-JlS10AQm/RzyrUGgl5irVkAlZYTJ99mNueUl+Qab+TcHhVedLiylWVkKBhRale+rS9yWIJK48JVzQlq3LcSdeA=="], + "postcss-nesting": ["postcss-nesting@14.0.0", "", { "dependencies": { "@csstools/selector-resolve-nested": "^4.0.0", "@csstools/selector-specificity": "^6.0.0", "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-YGFOfVrjxYfeGTS5XctP1WCI5hu8Lr9SmntjfRC+iX5hCihEO+QZl9Ra+pkjqkgoVdDKvb2JccpElcowhZtzpw=="], "postcss-normalize-charset": ["postcss-normalize-charset@5.1.0", "", { "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg=="], @@ -3883,19 +4155,19 @@ "postcss-normalize-whitespace": ["postcss-normalize-whitespace@5.1.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA=="], - "postcss-opacity-percentage": ["postcss-opacity-percentage@2.0.0", "", { "peerDependencies": { "postcss": "^8.2" } }, "sha512-lyDrCOtntq5Y1JZpBFzIWm2wG9kbEdujpNt4NLannF+J9c8CgFIzPa80YQfdza+Y+yFfzbYj/rfoOsYsooUWTQ=="], + "postcss-opacity-percentage": ["postcss-opacity-percentage@3.0.0", "", { "peerDependencies": { "postcss": "^8.4" } }, "sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ=="], "postcss-ordered-values": ["postcss-ordered-values@5.1.3", "", { "dependencies": { "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ=="], - "postcss-overflow-shorthand": ["postcss-overflow-shorthand@4.0.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-HQZ0qi/9iSYHW4w3ogNqVNr2J49DHJAl7r8O2p0Meip38jsdnRPgiDW7r/LlLrrMBMe3KHkvNtAV2UmRVxzLIg=="], + "postcss-overflow-shorthand": ["postcss-overflow-shorthand@7.0.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-9SLpjoUdGRoRrzoOdX66HbUs0+uDwfIAiXsRa7piKGOqPd6F4ZlON9oaDSP5r1Qpgmzw5L9Ht0undIK6igJPMA=="], "postcss-page-break": ["postcss-page-break@3.0.4", "", { "peerDependencies": { "postcss": "^8" } }, "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ=="], - "postcss-place": ["postcss-place@8.0.1", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-Ow2LedN8sL4pq8ubukO77phSVt4QyCm35ZGCYXKvRFayAwcpgB0sjNJglDoTuRdUL32q/ZC1VkPBo0AOEr4Uiw=="], + "postcss-place": ["postcss-place@11.0.0", "", { "dependencies": { "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-fAifpyjQ+fuDRp2nmF95WbotqbpjdazebedahXdfBxy5sHembOLpBQ1cHveZD9ZmjK26tYM8tikeNaUlp/KfHA=="], - "postcss-preset-env": ["postcss-preset-env@8.5.1", "", { "dependencies": { "@csstools/postcss-cascade-layers": "^3.0.1", "@csstools/postcss-color-function": "^2.2.3", "@csstools/postcss-color-mix-function": "^1.0.3", "@csstools/postcss-font-format-keywords": "^2.0.2", "@csstools/postcss-gradients-interpolation-method": "^3.0.6", "@csstools/postcss-hwb-function": "^2.2.2", "@csstools/postcss-ic-unit": "^2.0.4", "@csstools/postcss-is-pseudo-class": "^3.2.1", "@csstools/postcss-logical-float-and-clear": "^1.0.1", "@csstools/postcss-logical-resize": "^1.0.1", "@csstools/postcss-logical-viewport-units": "^1.0.3", "@csstools/postcss-media-minmax": "^1.0.4", "@csstools/postcss-media-queries-aspect-ratio-number-values": "^1.0.4", "@csstools/postcss-nested-calc": "^2.0.2", "@csstools/postcss-normalize-display-values": "^2.0.1", "@csstools/postcss-oklab-function": "^2.2.3", "@csstools/postcss-progressive-custom-properties": "^2.3.0", "@csstools/postcss-relative-color-syntax": "^1.0.2", "@csstools/postcss-scope-pseudo-class": "^2.0.2", "@csstools/postcss-stepped-value-functions": "^2.1.1", "@csstools/postcss-text-decoration-shorthand": "^2.2.4", "@csstools/postcss-trigonometric-functions": "^2.1.1", "@csstools/postcss-unset-value": "^2.0.1", "autoprefixer": "^10.4.14", "browserslist": "^4.21.9", "css-blank-pseudo": "^5.0.2", "css-has-pseudo": "^5.0.2", "css-prefers-color-scheme": "^8.0.2", "cssdb": "^7.6.0", "postcss-attribute-case-insensitive": "^6.0.2", "postcss-clamp": "^4.1.0", "postcss-color-functional-notation": "^5.1.0", "postcss-color-hex-alpha": "^9.0.2", "postcss-color-rebeccapurple": "^8.0.2", "postcss-custom-media": "^9.1.5", "postcss-custom-properties": "^13.2.0", "postcss-custom-selectors": "^7.1.3", "postcss-dir-pseudo-class": "^7.0.2", "postcss-double-position-gradients": "^4.0.4", "postcss-focus-visible": "^8.0.2", "postcss-focus-within": "^7.0.2", "postcss-font-variant": "^5.0.0", "postcss-gap-properties": "^4.0.1", "postcss-image-set-function": "^5.0.2", "postcss-initial": "^4.0.1", "postcss-lab-function": "^5.2.3", "postcss-logical": "^6.2.0", "postcss-nesting": "^11.3.0", "postcss-opacity-percentage": "^2.0.0", "postcss-overflow-shorthand": "^4.0.1", "postcss-page-break": "^3.0.4", "postcss-place": "^8.0.1", "postcss-pseudo-class-any-link": "^8.0.2", "postcss-replace-overflow-wrap": "^4.0.0", "postcss-selector-not": "^7.0.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-qhWnJJjP6ArLUINWJ38t6Aftxnv9NW6cXK0NuwcLCcRilbuw72dSFLkCVUJeCfHGgJiKzX+pnhkGiki0PEynWg=="], + "postcss-preset-env": ["postcss-preset-env@11.2.0", "", { "dependencies": { "@csstools/postcss-alpha-function": "^2.0.3", "@csstools/postcss-cascade-layers": "^6.0.0", "@csstools/postcss-color-function": "^5.0.2", "@csstools/postcss-color-function-display-p3-linear": "^2.0.2", "@csstools/postcss-color-mix-function": "^4.0.2", "@csstools/postcss-color-mix-variadic-function-arguments": "^2.0.2", "@csstools/postcss-content-alt-text": "^3.0.0", "@csstools/postcss-contrast-color-function": "^3.0.2", "@csstools/postcss-exponential-functions": "^3.0.1", "@csstools/postcss-font-format-keywords": "^5.0.0", "@csstools/postcss-font-width-property": "^1.0.0", "@csstools/postcss-gamut-mapping": "^3.0.2", "@csstools/postcss-gradients-interpolation-method": "^6.0.2", "@csstools/postcss-hwb-function": "^5.0.2", "@csstools/postcss-ic-unit": "^5.0.0", "@csstools/postcss-initial": "^3.0.0", "@csstools/postcss-is-pseudo-class": "^6.0.0", "@csstools/postcss-light-dark-function": "^3.0.0", "@csstools/postcss-logical-float-and-clear": "^4.0.0", "@csstools/postcss-logical-overflow": "^3.0.0", "@csstools/postcss-logical-overscroll-behavior": "^3.0.0", "@csstools/postcss-logical-resize": "^4.0.0", "@csstools/postcss-logical-viewport-units": "^4.0.0", "@csstools/postcss-media-minmax": "^3.0.1", "@csstools/postcss-media-queries-aspect-ratio-number-values": "^4.0.0", "@csstools/postcss-mixins": "^1.0.0", "@csstools/postcss-nested-calc": "^5.0.0", "@csstools/postcss-normalize-display-values": "^5.0.1", "@csstools/postcss-oklab-function": "^5.0.2", "@csstools/postcss-position-area-property": "^2.0.0", "@csstools/postcss-progressive-custom-properties": "^5.0.0", "@csstools/postcss-property-rule-prelude-list": "^2.0.0", "@csstools/postcss-random-function": "^3.0.1", "@csstools/postcss-relative-color-syntax": "^4.0.2", "@csstools/postcss-scope-pseudo-class": "^5.0.0", "@csstools/postcss-sign-functions": "^2.0.1", "@csstools/postcss-stepped-value-functions": "^5.0.1", "@csstools/postcss-syntax-descriptor-syntax-production": "^2.0.0", "@csstools/postcss-system-ui-font-family": "^2.0.0", "@csstools/postcss-text-decoration-shorthand": "^5.0.3", "@csstools/postcss-trigonometric-functions": "^5.0.1", "@csstools/postcss-unset-value": "^5.0.0", "autoprefixer": "^10.4.24", "browserslist": "^4.28.1", "css-blank-pseudo": "^8.0.1", "css-has-pseudo": "^8.0.0", "css-prefers-color-scheme": "^11.0.0", "cssdb": "^8.8.0", "postcss-attribute-case-insensitive": "^8.0.0", "postcss-clamp": "^4.1.0", "postcss-color-functional-notation": "^8.0.2", "postcss-color-hex-alpha": "^11.0.0", "postcss-color-rebeccapurple": "^11.0.0", "postcss-custom-media": "^12.0.1", "postcss-custom-properties": "^15.0.1", "postcss-custom-selectors": "^9.0.1", "postcss-dir-pseudo-class": "^10.0.0", "postcss-double-position-gradients": "^7.0.0", "postcss-focus-visible": "^11.0.0", "postcss-focus-within": "^10.0.0", "postcss-font-variant": "^5.0.0", "postcss-gap-properties": "^7.0.0", "postcss-image-set-function": "^8.0.0", "postcss-lab-function": "^8.0.2", "postcss-logical": "^9.0.0", "postcss-nesting": "^14.0.0", "postcss-opacity-percentage": "^3.0.0", "postcss-overflow-shorthand": "^7.0.0", "postcss-page-break": "^3.0.4", "postcss-place": "^11.0.0", "postcss-pseudo-class-any-link": "^11.0.0", "postcss-replace-overflow-wrap": "^4.0.0", "postcss-selector-not": "^9.0.0" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-eNYpuj68cjGjvZMoSAbHilaCt3yIyzBL1cVuSGJfvJewsaBW/U6dI2bqCJl3iuZsL+yvBobcy4zJFA/3I68IHQ=="], - "postcss-pseudo-class-any-link": ["postcss-pseudo-class-any-link@8.0.2", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-FYTIuRE07jZ2CW8POvctRgArQJ43yxhr5vLmImdKUvjFCkR09kh8pIdlCwdx/jbFm7MiW4QP58L4oOUv3grQYA=="], + "postcss-pseudo-class-any-link": ["postcss-pseudo-class-any-link@11.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-DNFZ4GMa3C3pU5dM+UCTG1CEeLtS1ZqV5DKSqCTJQMn1G5jnd/30fS8+A7H4o5bSD3MOcnx+VgI+xPE9Z5Wvig=="], "postcss-reduce-initial": ["postcss-reduce-initial@5.1.2", "", { "dependencies": { "browserslist": "^4.21.4", "caniuse-api": "^3.0.0" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg=="], @@ -3903,7 +4175,7 @@ "postcss-replace-overflow-wrap": ["postcss-replace-overflow-wrap@4.0.0", "", { "peerDependencies": { "postcss": "^8.0.3" } }, "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw=="], - "postcss-selector-not": ["postcss-selector-not@7.0.1", "", { "dependencies": { "postcss-selector-parser": "^6.0.10" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-1zT5C27b/zeJhchN7fP0kBr16Cc61mu7Si9uWWLoA3Px/D9tIJPKchJCkUH3tPO5D0pCFmGeApAv8XpXBQJ8SQ=="], + "postcss-selector-not": ["postcss-selector-not@9.0.0", "", { "dependencies": { "postcss-selector-parser": "^7.1.1" }, "peerDependencies": { "postcss": "^8.4" } }, "sha512-xhAtTdHnVU2M/CrpYOPyRUvg3njhVlKmn2GNYXDaRJV9Ygx4d5OkSkc7NINzjUqnbDFtaKXlISOBeyMXU/zyFQ=="], "postcss-selector-parser": ["postcss-selector-parser@6.0.15", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw=="], @@ -3937,7 +4209,7 @@ "property-information": ["property-information@6.4.1", "", {}, "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w=="], - "protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="], + "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], @@ -3945,8 +4217,6 @@ "pseudomap": ["pseudomap@1.0.2", "", {}, "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="], - "psl": ["psl@1.9.0", "", {}, "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="], - "pstree.remy": ["pstree.remy@1.1.8", "", {}, "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="], "public-encrypt": ["public-encrypt@4.0.3", "", { "dependencies": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", "randombytes": "^2.0.1", "safe-buffer": "^5.1.2" } }, "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q=="], @@ -3961,8 +4231,6 @@ "querystring-es3": ["querystring-es3@0.2.1", "", {}, "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA=="], - "querystringify": ["querystringify@2.2.0", "", {}, "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="], - "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], "random-bytes": ["random-bytes@1.0.0", "", {}, "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="], @@ -4003,23 +4271,21 @@ "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], - "react-lazy-load-image-component": ["react-lazy-load-image-component@1.6.0", "", { "dependencies": { "lodash.debounce": "^4.0.8", "lodash.throttle": "^4.1.1" }, "peerDependencies": { "react": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x", "react-dom": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x" } }, "sha512-8KFkDTgjh+0+PVbH+cx0AgxLGbdTsxWMnxXzU5HEUztqewk9ufQAu8cstjZhyvtMIPsdMcPZfA0WAa7HtjQbBQ=="], - "react-lifecycles-compat": ["react-lifecycles-compat@3.0.4", "", {}, "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="], "react-markdown": ["react-markdown@9.0.1", "", { "dependencies": { "@types/hast": "^3.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" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg=="], - "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="], + "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], - "react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="], + "react-remove-scroll": ["react-remove-scroll@2.5.5", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.3", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", "use-sidecar": "^1.1.2" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw=="], "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], "react-resizable-panels": ["react-resizable-panels@3.0.6", "", { "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-b3qKHQ3MLqOgSS+FRYKapNkJZf5EQzuf6+RLiq1/IlTHw99YrZ2NJZLk4hQIzTnnIkRg2LUqyVinu6YWWpUYew=="], - "react-router": ["react-router@6.22.0", "", { "dependencies": { "@remix-run/router": "1.15.0" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-q2yemJeg6gw/YixRlRnVx6IRJWZD6fonnfZhN1JIOhV2iJCPeRNSH3V1ISwHf+JWcESzLC3BOLD1T07tmO5dmg=="], + "react-router": ["react-router@6.30.3", "", { "dependencies": { "@remix-run/router": "1.23.2" }, "peerDependencies": { "react": ">=16.8" } }, "sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw=="], - "react-router-dom": ["react-router-dom@6.22.0", "", { "dependencies": { "@remix-run/router": "1.15.0", "react-router": "6.22.0" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag=="], + "react-router-dom": ["react-router-dom@6.30.3", "", { "dependencies": { "@remix-run/router": "1.23.2", "react-router": "6.30.3" }, "peerDependencies": { "react": ">=16.8", "react-dom": ">=16.8" } }, "sha512-pxPcv1AczD4vso7G4Z3TKcvlxK7g7TNt3/FNGMhfqyntocvYKj+GCatfigGDjbLozC4baguJ0ReCigoDJXb0ag=="], "react-speech-recognition": ["react-speech-recognition@3.10.0", "", { "peerDependencies": { "react": ">=16.8.0" } }, "sha512-EVSr4Ik8l9urwdPiK2r0+ADrLyDDrjB0qBRdUWO+w2MfwEBrj6NuRmy1GD3x7BU/V6/hab0pl8Lupen0zwlJyw=="], @@ -4035,8 +4301,6 @@ "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - "readable-web-to-node-stream": ["readable-web-to-node-stream@3.0.2", "", { "dependencies": { "readable-stream": "^3.6.0" } }, "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw=="], - "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], "recoil": ["recoil@0.7.7", "", { "dependencies": { "hamt_plus": "1.0.2" }, "peerDependencies": { "react": ">=16.13.1" } }, "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ=="], @@ -4057,8 +4321,6 @@ "regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="], - "regenerator-transform": ["regenerator-transform@0.15.2", "", { "dependencies": { "@babel/runtime": "^7.8.4" } }, "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg=="], - "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], "regexpu-core": ["regexpu-core@6.2.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" } }, "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA=="], @@ -4097,8 +4359,6 @@ "requireindex": ["requireindex@1.1.0", "", {}, "sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg=="], - "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="], - "resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="], "resolve-cwd": ["resolve-cwd@3.0.0", "", { "dependencies": { "resolve-from": "^5.0.0" } }, "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg=="], @@ -4115,11 +4375,13 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "rimraf": ["rimraf@6.1.2", "", { "dependencies": { "glob": "^13.0.0", "package-json-from-dist": "^1.0.1" }, "bin": "dist/esm/bin.mjs" }, "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g=="], + "rimraf": ["rimraf@6.1.3", "", { "dependencies": { "glob": "^13.0.3", "package-json-from-dist": "^1.0.1" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA=="], "ripemd160": ["ripemd160@2.0.2", "", { "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" } }, "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA=="], - "rollup": ["rollup@4.37.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.37.0", "@rollup/rollup-android-arm64": "4.37.0", "@rollup/rollup-darwin-arm64": "4.37.0", "@rollup/rollup-darwin-x64": "4.37.0", "@rollup/rollup-freebsd-arm64": "4.37.0", "@rollup/rollup-freebsd-x64": "4.37.0", "@rollup/rollup-linux-arm-gnueabihf": "4.37.0", "@rollup/rollup-linux-arm-musleabihf": "4.37.0", "@rollup/rollup-linux-arm64-gnu": "4.37.0", "@rollup/rollup-linux-arm64-musl": "4.37.0", "@rollup/rollup-linux-loongarch64-gnu": "4.37.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.37.0", "@rollup/rollup-linux-riscv64-gnu": "4.37.0", "@rollup/rollup-linux-riscv64-musl": "4.37.0", "@rollup/rollup-linux-s390x-gnu": "4.37.0", "@rollup/rollup-linux-x64-gnu": "4.37.0", "@rollup/rollup-linux-x64-musl": "4.37.0", "@rollup/rollup-win32-arm64-msvc": "4.37.0", "@rollup/rollup-win32-ia32-msvc": "4.37.0", "@rollup/rollup-win32-x64-msvc": "4.37.0", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg=="], + "robust-predicates": ["robust-predicates@3.0.2", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="], + + "rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="], "rollup-plugin-peer-deps-external": ["rollup-plugin-peer-deps-external@2.2.4", "", { "peerDependencies": { "rollup": "*" } }, "sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g=="], @@ -4129,6 +4391,8 @@ "rollup-pluginutils": ["rollup-pluginutils@2.8.2", "", { "dependencies": { "estree-walker": "^0.6.1" } }, "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ=="], + "roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="], + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], "rrweb-cssom": ["rrweb-cssom@0.8.0", "", {}, "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw=="], @@ -4137,6 +4401,8 @@ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], + "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="], + "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], @@ -4157,15 +4423,13 @@ "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], - "schema-utils": ["schema-utils@3.3.0", "", { "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } }, "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg=="], - "seedrandom": ["seedrandom@3.0.5", "", {}, "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="], "semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "send": ["send@1.2.0", "", { "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" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], - "serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="], + "serialize-javascript": ["serialize-javascript@7.0.4", "", {}, "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg=="], "serve-static": ["serve-static@2.2.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ=="], @@ -4237,6 +4501,8 @@ "standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="], + "state-local": ["state-local@1.0.7", "", {}, "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w=="], + "static-browser-server": ["static-browser-server@1.0.3", "", { "dependencies": { "@open-draft/deferred-promise": "^2.1.0", "dotenv": "^16.0.3", "mime-db": "^1.52.0", "outvariant": "^1.3.0" } }, "sha512-ZUyfgGDdFRbZGGJQ1YhiM930Yczz5VlbJObrQLlk24+qNHVQx4OlLcYswEUo3bIyNAbQUIUR9Yr5/Hqjzqb4zA=="], "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], @@ -4297,9 +4563,9 @@ "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - "strnum": ["strnum@1.0.5", "", {}, "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="], + "strnum": ["strnum@2.2.0", "", {}, "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg=="], - "strtok3": ["strtok3@7.0.0", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "peek-readable": "^5.0.0" } }, "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ=="], + "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], "style-inject": ["style-inject@0.3.0", "", {}, "sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw=="], @@ -4309,6 +4575,8 @@ "stylehacks": ["stylehacks@5.1.1", "", { "dependencies": { "browserslist": "^4.21.4", "postcss-selector-parser": "^6.0.4" }, "peerDependencies": { "postcss": "^8.2.15" } }, "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw=="], + "stylis": ["stylis@4.3.6", "", {}, "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ=="], + "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="], "superagent": ["superagent@9.0.2", "", { "dependencies": { "component-emitter": "^1.3.0", "cookiejar": "^2.1.4", "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", "formidable": "^3.5.1", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0" } }, "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w=="], @@ -4321,7 +4589,9 @@ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - "svgo": ["svgo@2.8.0", "", { "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^4.1.3", "css-tree": "^1.1.3", "csso": "^4.2.0", "picocolors": "^1.0.0", "stable": "^0.1.8" }, "bin": "bin/svgo" }, "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg=="], + "svgo": ["svgo@2.8.2", "", { "dependencies": { "commander": "^7.2.0", "css-select": "^4.1.3", "css-tree": "^1.1.3", "csso": "^4.2.0", "picocolors": "^1.0.0", "sax": "^1.5.0", "stable": "^0.1.8" }, "bin": "./bin/svgo" }, "sha512-TyzE4NVGLUFy+H/Uy4N6c3G0HEeprsVfge6Lmq+0FdQQ/zqoVYB62IsBZORsiL+o96s6ff/V6/3UQo/C0cgCAA=="], + + "swr": ["swr@2.4.1", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA=="], "symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="], @@ -4349,8 +4619,6 @@ "terser": ["terser@5.27.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": "bin/terser" }, "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A=="], - "terser-webpack-plugin": ["terser-webpack-plugin@5.3.10", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.1", "terser": "^5.26.0" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w=="], - "test-exclude": ["test-exclude@6.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" } }, "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w=="], "text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="], @@ -4361,13 +4629,13 @@ "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], - "tiktoken": ["tiktoken@1.0.15", "", {}, "sha512-sCsrq/vMWUSEW29CJLNmPvWxlVp7yh2tlkAjpJltIKqp5CKf98ZNpdeHRmAlPVFlGEbswDc6SmI8vz64W/qErw=="], - "timers-browserify": ["timers-browserify@2.0.12", "", { "dependencies": { "setimmediate": "^1.0.4" } }, "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ=="], "tiny-emitter": ["tiny-emitter@2.1.0", "", {}, "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="], - "tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="], + "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], "tldts": ["tldts@6.1.86", "", { "dependencies": { "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ=="], @@ -4383,7 +4651,7 @@ "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], - "token-types": ["token-types@5.0.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg=="], + "token-types": ["token-types@6.1.2", "", { "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww=="], "touch": ["touch@3.1.0", "", { "dependencies": { "nopt": "~1.0.10" }, "bin": { "nodetouch": "bin/nodetouch.js" } }, "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA=="], @@ -4403,8 +4671,12 @@ "ts-api-utils": ["ts-api-utils@2.0.1", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w=="], + "ts-dedent": ["ts-dedent@2.2.0", "", {}, "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ=="], + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], + "ts-md5": ["ts-md5@1.3.1", "", {}, "sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg=="], + "ts-node": ["ts-node@10.9.2", "", { "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" }, "peerDependencies": { "@swc/core": ">=1.2.50", "@swc/wasm": ">=1.2.50", "@types/node": "*", "typescript": ">=2.7" }, "optionalPeers": ["@swc/core", "@swc/wasm"], "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" } }, "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ=="], "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], @@ -4413,6 +4685,20 @@ "tty-browserify": ["tty-browserify@0.0.1", "", {}, "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw=="], + "turbo": ["turbo@2.8.14", "", { "optionalDependencies": { "turbo-darwin-64": "2.8.14", "turbo-darwin-arm64": "2.8.14", "turbo-linux-64": "2.8.14", "turbo-linux-arm64": "2.8.14", "turbo-windows-64": "2.8.14", "turbo-windows-arm64": "2.8.14" }, "bin": { "turbo": "bin/turbo" } }, "sha512-UCTxeMNYT1cKaHiIFdLCQ7ulI+jw5i5uOnJOrRXsgUD7G3+OjlUjwVd7JfeVt2McWSVGjYA3EVW/v1FSsJ5DtA=="], + + "turbo-darwin-64": ["turbo-darwin-64@2.8.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-9sFi7n2lLfEsGWi5OEoA/eTtQU2BPKtzSYKqufMtDeRmqMT9vKjbv9gJCRkllSVE9BOXA0qXC3diyX8V8rKIKw=="], + + "turbo-darwin-arm64": ["turbo-darwin-arm64@2.8.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aS4yJuy6A1PCLws+PJpZP0qCURG8Y5iVx13z/WAbKyeDTY6W6PiGgcEllSaeLGxyn++382ztN/EZH85n2zZ6VQ=="], + + "turbo-linux-64": ["turbo-linux-64@2.8.14", "", { "os": "linux", "cpu": "x64" }, "sha512-XC6wPUDJkakjhNLaS0NrHDMiujRVjH+naEAwvKLArgqRaFkNxjmyNDRM4eu3soMMFmjym6NTxYaF74rvET+Orw=="], + + "turbo-linux-arm64": ["turbo-linux-arm64@2.8.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-ChfE7isyVNjZrVSPDwcfqcHLG/FuIBbOFxnt1FM8vSuBGzHAs8AlTdwFNIxlEMJfZ8Ad9mdMxdmsCUPIWiQ6cg=="], + + "turbo-windows-64": ["turbo-windows-64@2.8.14", "", { "os": "win32", "cpu": "x64" }, "sha512-FTbIeQL1ycLFW2t9uQNMy+bRSzi3Xhwun/e7ZhFBdM+U0VZxxrtfYEBM9CHOejlfqomk6Jh7aRz0sJoqYn39Hg=="], + + "turbo-windows-arm64": ["turbo-windows-arm64@2.8.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-KgZX12cTyhY030qS7ieT8zRkhZZE2VWJasDFVUSVVn17nR7IShpv68/7j5UqJNeRLIGF1XPK0phsP5V5yw3how=="], + "type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], @@ -4441,19 +4727,25 @@ "ua-parser-js": ["ua-parser-js@1.0.37", "", {}, "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ=="], + "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], + "uglify-js": ["uglify-js@3.17.4", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g=="], "uid-safe": ["uid-safe@2.1.5", "", { "dependencies": { "random-bytes": "~1.0.0" } }, "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA=="], "uid2": ["uid2@0.0.4", "", {}, "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="], + "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], + "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], "undefsafe": ["undefsafe@2.0.5", "", {}, "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="], - "undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="], + "underscore": ["underscore@1.13.8", "", {}, "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ=="], - "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "undici": ["undici@7.24.4", "", {}, "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="], @@ -4495,10 +4787,6 @@ "url": ["url@0.11.4", "", { "dependencies": { "punycode": "^1.4.1", "qs": "^6.12.3" } }, "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg=="], - "url-parse": ["url-parse@1.5.10", "", { "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ=="], - - "url-template": ["url-template@2.0.8", "", {}, "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw=="], - "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], "use-composed-ref": ["use-composed-ref@1.3.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ=="], @@ -4535,36 +4823,42 @@ "vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="], - "vite": ["vite@6.4.1", "", { "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" }, "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" }, "optionalPeers": ["less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "tsx"], "bin": "bin/vite.js" }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], + "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], "vite-plugin-compression2": ["vite-plugin-compression2@2.2.1", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0", "tar-mini": "^0.2.0" } }, "sha512-LMDkgheJaFBmb8cB8ymgUpXHXnd3m4kmjEInvp59fOZMSaT/9oDUtqpO0ihr4ExGsnWfYcRe13/TNN3BEk2t/g=="], - "vite-plugin-node-polyfills": ["vite-plugin-node-polyfills@0.23.0", "", { "dependencies": { "@rollup/plugin-inject": "^5.0.5", "node-stdlib-browser": "^1.2.0" }, "peerDependencies": { "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-4n+Ys+2bKHQohPBKigFlndwWQ5fFKwaGY6muNDMTb0fSQLyBzS+jjUNRZG9sKF0S/Go4ApG6LFnUGopjkILg3w=="], + "vite-plugin-node-polyfills": ["vite-plugin-node-polyfills@0.25.0", "", { "dependencies": { "@rollup/plugin-inject": "^5.0.5", "node-stdlib-browser": "^1.3.1" }, "peerDependencies": { "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-rHZ324W3LhfGPxWwQb2N048TThB6nVvnipsqBUJEzh3R9xeK9KI3si+GMQxCuAcpPJBVf0LpDtJ+beYzB3/chg=="], - "vite-plugin-pwa": ["vite-plugin-pwa@0.21.2", "", { "dependencies": { "debug": "^4.3.6", "pretty-bytes": "^6.1.1", "tinyglobby": "^0.2.10", "workbox-build": "^7.3.0", "workbox-window": "^7.3.0" }, "peerDependencies": { "@vite-pwa/assets-generator": "^0.2.6", "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", "workbox-build": "^7.3.0", "workbox-window": "^7.3.0" }, "optionalPeers": ["@vite-pwa/assets-generator"] }, "sha512-vFhH6Waw8itNu37hWUJxL50q+CBbNcMVzsKaYHQVrfxTt3ihk3PeLO22SbiP1UNWzcEPaTQv+YVxe4G0KOjAkg=="], + "vite-plugin-pwa": ["vite-plugin-pwa@1.2.0", "", { "dependencies": { "debug": "^4.3.6", "pretty-bytes": "^6.1.1", "tinyglobby": "^0.2.10", "workbox-build": "^7.4.0", "workbox-window": "^7.4.0" }, "peerDependencies": { "@vite-pwa/assets-generator": "^1.0.0", "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@vite-pwa/assets-generator"] }, "sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw=="], "vm-browserify": ["vm-browserify@1.1.2", "", {}, "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ=="], "void-elements": ["void-elements@3.1.0", "", {}, "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="], + "vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="], + + "vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="], + + "vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="], + + "vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="], + + "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + + "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], + "w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="], "w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="], "walker": ["walker@1.0.8", "", { "dependencies": { "makeerror": "1.0.12" } }, "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ=="], - "watchpack": ["watchpack@2.4.2", "", { "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" } }, "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw=="], - "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], - "webpack": ["webpack@5.94.0", "", { "dependencies": { "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.10", "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": "bin/webpack.js" }, "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg=="], - - "webpack-sources": ["webpack-sources@3.2.3", "", {}, "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w=="], - "websocket-driver": ["websocket-driver@0.7.4", "", { "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", "websocket-extensions": ">=0.1.1" } }, "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg=="], "websocket-extensions": ["websocket-extensions@0.1.4", "", {}, "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg=="], @@ -4595,37 +4889,37 @@ "wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="], - "workbox-background-sync": ["workbox-background-sync@7.3.0", "", { "dependencies": { "idb": "^7.0.1", "workbox-core": "7.3.0" } }, "sha512-PCSk3eK7Mxeuyatb22pcSx9dlgWNv3+M8PqPaYDokks8Y5/FX4soaOqj3yhAZr5k6Q5JWTOMYgaJBpbw11G9Eg=="], + "workbox-background-sync": ["workbox-background-sync@7.4.0", "", { "dependencies": { "idb": "^7.0.1", "workbox-core": "7.4.0" } }, "sha512-8CB9OxKAgKZKyNMwfGZ1XESx89GryWTfI+V5yEj8sHjFH8MFelUwYXEyldEK6M6oKMmn807GoJFUEA1sC4XS9w=="], - "workbox-broadcast-update": ["workbox-broadcast-update@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-T9/F5VEdJVhwmrIAE+E/kq5at2OY6+OXXgOWQevnubal6sO92Gjo24v6dCVwQiclAF5NS3hlmsifRrpQzZCdUA=="], + "workbox-broadcast-update": ["workbox-broadcast-update@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-+eZQwoktlvo62cI0b+QBr40v5XjighxPq3Fzo9AWMiAosmpG5gxRHgTbGGhaJv/q/MFVxwFNGh/UwHZ/8K88lA=="], - "workbox-build": ["workbox-build@7.3.0", "", { "dependencies": { "@apideck/better-ajv-errors": "^0.3.1", "@babel/core": "^7.24.4", "@babel/preset-env": "^7.11.0", "@babel/runtime": "^7.11.2", "@rollup/plugin-babel": "^5.2.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^2.4.1", "@rollup/plugin-terser": "^0.4.3", "@surma/rollup-plugin-off-main-thread": "^2.2.3", "ajv": "^8.6.0", "common-tags": "^1.8.0", "fast-json-stable-stringify": "^2.1.0", "fs-extra": "^9.0.1", "glob": "^7.1.6", "lodash": "^4.17.20", "pretty-bytes": "^5.3.0", "rollup": "^2.43.1", "source-map": "^0.8.0-beta.0", "stringify-object": "^3.3.0", "strip-comments": "^2.0.1", "tempy": "^0.6.0", "upath": "^1.2.0", "workbox-background-sync": "7.3.0", "workbox-broadcast-update": "7.3.0", "workbox-cacheable-response": "7.3.0", "workbox-core": "7.3.0", "workbox-expiration": "7.3.0", "workbox-google-analytics": "7.3.0", "workbox-navigation-preload": "7.3.0", "workbox-precaching": "7.3.0", "workbox-range-requests": "7.3.0", "workbox-recipes": "7.3.0", "workbox-routing": "7.3.0", "workbox-strategies": "7.3.0", "workbox-streams": "7.3.0", "workbox-sw": "7.3.0", "workbox-window": "7.3.0" } }, "sha512-JGL6vZTPlxnlqZRhR/K/msqg3wKP+m0wfEUVosK7gsYzSgeIxvZLi1ViJJzVL7CEeI8r7rGFV973RiEqkP3lWQ=="], + "workbox-build": ["workbox-build@7.4.0", "", { "dependencies": { "@apideck/better-ajv-errors": "^0.3.1", "@babel/core": "^7.24.4", "@babel/preset-env": "^7.11.0", "@babel/runtime": "^7.11.2", "@rollup/plugin-babel": "^5.2.0", "@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-replace": "^2.4.1", "@rollup/plugin-terser": "^0.4.3", "@surma/rollup-plugin-off-main-thread": "^2.2.3", "ajv": "^8.6.0", "common-tags": "^1.8.0", "fast-json-stable-stringify": "^2.1.0", "fs-extra": "^9.0.1", "glob": "^11.0.1", "lodash": "^4.17.20", "pretty-bytes": "^5.3.0", "rollup": "^2.79.2", "source-map": "^0.8.0-beta.0", "stringify-object": "^3.3.0", "strip-comments": "^2.0.1", "tempy": "^0.6.0", "upath": "^1.2.0", "workbox-background-sync": "7.4.0", "workbox-broadcast-update": "7.4.0", "workbox-cacheable-response": "7.4.0", "workbox-core": "7.4.0", "workbox-expiration": "7.4.0", "workbox-google-analytics": "7.4.0", "workbox-navigation-preload": "7.4.0", "workbox-precaching": "7.4.0", "workbox-range-requests": "7.4.0", "workbox-recipes": "7.4.0", "workbox-routing": "7.4.0", "workbox-strategies": "7.4.0", "workbox-streams": "7.4.0", "workbox-sw": "7.4.0", "workbox-window": "7.4.0" } }, "sha512-Ntk1pWb0caOFIvwz/hfgrov/OJ45wPEhI5PbTywQcYjyZiVhT3UrwwUPl6TRYbTm4moaFYithYnl1lvZ8UjxcA=="], - "workbox-cacheable-response": ["workbox-cacheable-response@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-eAFERIg6J2LuyELhLlmeRcJFa5e16Mj8kL2yCDbhWE+HUun9skRQrGIFVUagqWj4DMaaPSMWfAolM7XZZxNmxA=="], + "workbox-cacheable-response": ["workbox-cacheable-response@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-0Fb8795zg/x23ISFkAc7lbWes6vbw34DGFIMw31cwuHPgDEC/5EYm6m/ZkylLX0EnEbbOyOCLjKgFS/Z5g0HeQ=="], - "workbox-core": ["workbox-core@7.3.0", "", {}, "sha512-Z+mYrErfh4t3zi7NVTvOuACB0A/jA3bgxUN3PwtAVHvfEsZxV9Iju580VEETug3zYJRc0Dmii/aixI/Uxj8fmw=="], + "workbox-core": ["workbox-core@7.4.0", "", {}, "sha512-6BMfd8tYEnN4baG4emG9U0hdXM4gGuDU3ectXuVHnj71vwxTFI7WOpQJC4siTOlVtGqCUtj0ZQNsrvi6kZZTAQ=="], - "workbox-expiration": ["workbox-expiration@7.3.0", "", { "dependencies": { "idb": "^7.0.1", "workbox-core": "7.3.0" } }, "sha512-lpnSSLp2BM+K6bgFCWc5bS1LR5pAwDWbcKt1iL87/eTSJRdLdAwGQznZE+1czLgn/X05YChsrEegTNxjM067vQ=="], + "workbox-expiration": ["workbox-expiration@7.4.0", "", { "dependencies": { "idb": "^7.0.1", "workbox-core": "7.4.0" } }, "sha512-V50p4BxYhtA80eOvulu8xVfPBgZbkxJ1Jr8UUn0rvqjGhLDqKNtfrDfjJKnLz2U8fO2xGQJTx/SKXNTzHOjnHw=="], - "workbox-google-analytics": ["workbox-google-analytics@7.3.0", "", { "dependencies": { "workbox-background-sync": "7.3.0", "workbox-core": "7.3.0", "workbox-routing": "7.3.0", "workbox-strategies": "7.3.0" } }, "sha512-ii/tSfFdhjLHZ2BrYgFNTrb/yk04pw2hasgbM70jpZfLk0vdJAXgaiMAWsoE+wfJDNWoZmBYY0hMVI0v5wWDbg=="], + "workbox-google-analytics": ["workbox-google-analytics@7.4.0", "", { "dependencies": { "workbox-background-sync": "7.4.0", "workbox-core": "7.4.0", "workbox-routing": "7.4.0", "workbox-strategies": "7.4.0" } }, "sha512-MVPXQslRF6YHkzGoFw1A4GIB8GrKym/A5+jYDUSL+AeJw4ytQGrozYdiZqUW1TPQHW8isBCBtyFJergUXyNoWQ=="], - "workbox-navigation-preload": ["workbox-navigation-preload@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-fTJzogmFaTv4bShZ6aA7Bfj4Cewaq5rp30qcxl2iYM45YD79rKIhvzNHiFj1P+u5ZZldroqhASXwwoyusnr2cg=="], + "workbox-navigation-preload": ["workbox-navigation-preload@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-etzftSgdQfjMcfPgbfaZCfM2QuR1P+4o8uCA2s4rf3chtKTq/Om7g/qvEOcZkG6v7JZOSOxVYQiOu6PbAZgU6w=="], - "workbox-precaching": ["workbox-precaching@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0", "workbox-routing": "7.3.0", "workbox-strategies": "7.3.0" } }, "sha512-ckp/3t0msgXclVAYaNndAGeAoWQUv7Rwc4fdhWL69CCAb2UHo3Cef0KIUctqfQj1p8h6aGyz3w8Cy3Ihq9OmIw=="], + "workbox-precaching": ["workbox-precaching@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0", "workbox-routing": "7.4.0", "workbox-strategies": "7.4.0" } }, "sha512-VQs37T6jDqf1rTxUJZXRl3yjZMf5JX/vDPhmx2CPgDDKXATzEoqyRqhYnRoxl6Kr0rqaQlp32i9rtG5zTzIlNg=="], - "workbox-range-requests": ["workbox-range-requests@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-EyFmM1KpDzzAouNF3+EWa15yDEenwxoeXu9bgxOEYnFfCxns7eAxA9WSSaVd8kujFFt3eIbShNqa4hLQNFvmVQ=="], + "workbox-range-requests": ["workbox-range-requests@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-3Vq854ZNuP6Y0KZOQWLaLC9FfM7ZaE+iuQl4VhADXybwzr4z/sMmnLgTeUZLq5PaDlcJBxYXQ3U91V7dwAIfvw=="], - "workbox-recipes": ["workbox-recipes@7.3.0", "", { "dependencies": { "workbox-cacheable-response": "7.3.0", "workbox-core": "7.3.0", "workbox-expiration": "7.3.0", "workbox-precaching": "7.3.0", "workbox-routing": "7.3.0", "workbox-strategies": "7.3.0" } }, "sha512-BJro/MpuW35I/zjZQBcoxsctgeB+kyb2JAP5EB3EYzePg8wDGoQuUdyYQS+CheTb+GhqJeWmVs3QxLI8EBP1sg=="], + "workbox-recipes": ["workbox-recipes@7.4.0", "", { "dependencies": { "workbox-cacheable-response": "7.4.0", "workbox-core": "7.4.0", "workbox-expiration": "7.4.0", "workbox-precaching": "7.4.0", "workbox-routing": "7.4.0", "workbox-strategies": "7.4.0" } }, "sha512-kOkWvsAn4H8GvAkwfJTbwINdv4voFoiE9hbezgB1sb/0NLyTG4rE7l6LvS8lLk5QIRIto+DjXLuAuG3Vmt3cxQ=="], - "workbox-routing": ["workbox-routing@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-ZUlysUVn5ZUzMOmQN3bqu+gK98vNfgX/gSTZ127izJg/pMMy4LryAthnYtjuqcjkN4HEAx1mdgxNiKJMZQM76A=="], + "workbox-routing": ["workbox-routing@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-C/ooj5uBWYAhAqwmU8HYQJdOjjDKBp9MzTQ+otpMmd+q0eF59K+NuXUek34wbL0RFrIXe/KKT+tUWcZcBqxbHQ=="], - "workbox-strategies": ["workbox-strategies@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0" } }, "sha512-tmZydug+qzDFATwX7QiEL5Hdf7FrkhjaF9db1CbB39sDmEZJg3l9ayDvPxy8Y18C3Y66Nrr9kkN1f/RlkDgllg=="], + "workbox-strategies": ["workbox-strategies@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0" } }, "sha512-T4hVqIi5A4mHi92+5EppMX3cLaVywDp8nsyUgJhOZxcfSV/eQofcOA6/EMo5rnTNmNTpw0rUgjAI6LaVullPpg=="], - "workbox-streams": ["workbox-streams@7.3.0", "", { "dependencies": { "workbox-core": "7.3.0", "workbox-routing": "7.3.0" } }, "sha512-SZnXucyg8x2Y61VGtDjKPO5EgPUG5NDn/v86WYHX+9ZqvAsGOytP0Jxp1bl663YUuMoXSAtsGLL+byHzEuMRpw=="], + "workbox-streams": ["workbox-streams@7.4.0", "", { "dependencies": { "workbox-core": "7.4.0", "workbox-routing": "7.4.0" } }, "sha512-QHPBQrey7hQbnTs5GrEVoWz7RhHJXnPT+12qqWM378orDMo5VMJLCkCM1cnCk+8Eq92lccx/VgRZ7WAzZWbSLg=="], - "workbox-sw": ["workbox-sw@7.3.0", "", {}, "sha512-aCUyoAZU9IZtH05mn0ACUpyHzPs0lMeJimAYkQkBsOWiqaJLgusfDCR+yllkPkFRxWpZKF8vSvgHYeG7LwhlmA=="], + "workbox-sw": ["workbox-sw@7.4.0", "", {}, "sha512-ltU+Kr3qWR6BtbdlMnCjobZKzeV1hN+S6UvDywBrwM19TTyqA03X66dzw1tEIdJvQ4lYKkBFox6IAEhoSEZ8Xw=="], - "workbox-window": ["workbox-window@7.3.0", "", { "dependencies": { "@types/trusted-types": "^2.0.2", "workbox-core": "7.3.0" } }, "sha512-qW8PDy16OV1UBaUNGlTVcepzrlzyzNW/ZJvFQQs2j2TzGsg6IKjcpZC1RSquqQnTOafl5pCj5bGfAHlCjOOjdA=="], + "workbox-window": ["workbox-window@7.4.0", "", { "dependencies": { "@types/trusted-types": "^2.0.2", "workbox-core": "7.4.0" } }, "sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw=="], "wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], @@ -4637,6 +4931,8 @@ "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + "xlsx": ["xlsx@https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", { "bin": { "xlsx": "./bin/xlsx.njs" } }], + "xml": ["xml@1.0.1", "", {}, "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw=="], "xml-crypto": ["xml-crypto@6.1.2", "", { "dependencies": { "@xmldom/is-dom-node": "^1.0.1", "@xmldom/xmldom": "^0.8.10", "xpath": "^0.0.33" } }, "sha512-leBOVQdVi8FvPJrMYoum7Ici9qyxfE4kVi+AkpUoYCSXaQF4IlBm1cneTK9oAxR61LpYxTx7lNcsnBIeRpGW2w=="], @@ -4647,7 +4943,7 @@ "xml2js": ["xml2js@0.6.2", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA=="], - "xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], + "xmlbuilder": ["xmlbuilder@10.1.1", "", {}, "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg=="], "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], @@ -4671,11 +4967,9 @@ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - "youtube-transcript": ["youtube-transcript@1.2.1", "", {}, "sha512-TvEGkBaajKw+B6y91ziLuBLsa5cawgowou+Bk0ciGpjELDfAzSzTGXaZmeSSkUeknCPpEr/WGApOHDwV7V+Y9Q=="], - "zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="], - "zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="], + "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], @@ -4735,6 +5029,12 @@ "@aws-sdk/client-bedrock-agent-runtime/@smithy/core": ["@smithy/core@3.17.2", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-stream": "^4.5.5", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-n3g4Nl1Te+qGPDbNFAYf+smkRVB+JhFsGy9uJXXZQEufoP4u0r+WLh6KvTDolCswaagysDc/afS1yvb2jnj1gQ=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-d5T7ZS3J/r8P/PDjgmCcutmNxnSRvPH1U6iHeXjzI50sMr78GLmFcrczLw33Ap92oEKqa4CLrkAPeSSOqvGdUA=="], + + "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-lxfDT0UuSc1HqltOGsTEAlZ6H29gpfDSdEPTapD5G63RbnYToZ+ezjzdonCCH90j5tRRCw3aLXVbiZaBW3VRVg=="], + + "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-TPhiGByWnYyzcpU/K3pO5V7QgtXYpE0NaJPEZBCa1Y5jlw5SjqzMSbFiLb+ZkJhqoQc0ImGyVINqnq1ze0ZRcQ=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="], "@aws-sdk/client-bedrock-agent-runtime/@smithy/hash-node": ["@smithy/hash-node@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kKU0gVhx/ppVMntvUOZE7WRMFW86HuaxLwvqileBEjL7PoILI8/djoILw3gPQloGVE6O0oOzqafxeNi2KbnUJw=="], @@ -4755,8 +5055,12 @@ "@aws-sdk/client-bedrock-agent-runtime/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], "@aws-sdk/client-bedrock-agent-runtime/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], @@ -4771,88 +5075,12 @@ "@aws-sdk/client-bedrock-agent-runtime/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="], "@aws-sdk/client-bedrock-agent-runtime/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - "@aws-sdk/client-bedrock-runtime/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.952.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-ini": "3.952.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.952.0", "@aws-sdk/credential-provider-web-identity": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-pj7nidLrb3Dz9llcUPh6N0Yv1dBYTS9xJqi8u0kI8D5sn72HJMB+fIOhcDQVXXAw/dpVolOAH9FOAbog5JDAMg=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.948.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Qa8Zj+EAqA0VlAVvxpRnpBpIWJI9KUwaioY1vkeNVwXPlNaz9y9zCKVM9iU9OZ5HXpoUg6TnhATAHXHAE8+QsQ=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.7", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.947.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/config-resolver": ["@smithy/config-resolver@4.4.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/types": "^4.10.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-s3U5ChS21DwU54kMmZ0UJumoS5cg0+rGVZvN6f5Lp6EbAVi0ZyP+qDSHdewfmXKUgNK1j3z45JyzulkDukrjAA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/core": ["@smithy/core@3.19.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.7", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-stream": "^4.5.7", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Y9oHXpBcXQgYHOcAEmxjkDilUbSTkgKjoHYed3WaYUH8jngq8lPWDBSpjHblJ9uOgBdy5mh3pzebrScDdYr29w=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.6", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-6OiaAaEbLB6dEkRbQyNzFSJv5HDvly3Mc6q/qcPd2uS/g3szR8wAIkh7UndAFKfMypNSTuZ6eCBmgCLR5LacTg=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-xP5YXbOVRVN8A4pDnSUkEUsL9fYFU6VNhxo8tgr13YnMbf3Pn4xVr+hSyLVjS1Frfi1Uk03ET5Bwml4+0CeYEw=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.6", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-jhH7nJuaOpnTFcuZpWK9dqb6Ge2yGi1okTo0W6wkJrfwAm2vwmO74tF1v07JmrSyHBcKLQATEexclJw9K1Vj7w=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/hash-node": ["@smithy/hash-node@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-k3Dy9VNR37wfMh2/1RHkFf/e0rMyN0pjY0FdyY6ItJRjENYyVPRMwad6ZR1S9HFm6tTuIOd9pqKBmtJ4VHxvxg=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-E4t/V/q2T46RY21fpfznd1iSLTvCXKNKo4zJ1QuEFN4SE9gKfu2vb6bgq35LpufkQ+SETWIC7ZAf2GGvTlBaMQ=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-0cjqjyfj+Gls30ntq45SsBtqF3dfJQCeqQPyGz58Pk8OgrAr5YiB7ZvDzjCA94p4r6DCI4qLm7FKobqBjf515w=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.0", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-serde": "^4.2.7", "@smithy/node-config-provider": "^4.3.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-M6qWfUNny6NFNy8amrCGIb9TfOMUkHVtg9bHtEFGRgfH7A7AtPpn/fcrToGPjVDK1ECuMVvqGQOXcZxmu9K+7A=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.16", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/protocol-http": "^5.3.6", "@smithy/service-error-classification": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-retry": "^4.2.6", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-XPpNhNRzm3vhYm7YCsyw3AtmWggJbg1wNGAoqb7NBYr5XA5isMRv14jgbYyUV6IvbTBFZQdf2QpeW43LrRdStQ=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-JSbALU3G+JS4kyBZPqnJ3hxIYwOVRV7r9GNQMS6j5VsQDo5+Es5nddLfr9TQlxZLNHPvKSh+XSB0OuWGfSWFcA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-fYEyL59Qe82Ha1p97YQTMEQPJYmBS+ux76foqluaTVWoG9Px5J53w6NvXZNE3wP7lIicLDF7Vj1Em18XTX7fsA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/smithy-client": ["@smithy/smithy-client@4.10.1", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-endpoint": "^4.4.0", "@smithy/middleware-stack": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-stream": "^4.5.7", "tslib": "^2.6.2" } }, "sha512-1ovWdxzYprhq+mWqiGZlt3kF69LJthuQcfY9BIyHx9MywTFKzFapluku1QXoaBB43GCsLDxNqS+1v30ure69AA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/url-parser": ["@smithy/url-parser@4.2.6", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tVoyzJ2vXp4R3/aeV4EQjBDmCuWxRa8eo3KybL7Xv4wEM16nObYh7H1sNfcuLWHAAAzb0RVyxUz1S3sGj4X+Tg=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.15", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-LiZQVAg/oO8kueX4c+oMls5njaD2cRLXRfcjlTYjhIqmwHnCwkQO5B3dMQH0c5PACILxGAQf6Mxsq7CjlDc76A=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.18", "", { "dependencies": { "@smithy/config-resolver": "^4.4.4", "@smithy/credential-provider-imds": "^4.2.6", "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-Kw2J+KzYm9C9Z9nY6+W0tEnoZOofstVCMTshli9jhQbQCy64rueGfKzPfuFBnVUqZD9JobxTh2DzHmPkp/Va/Q=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-v60VNM2+mPvgHCBXEfMCYrQ0RepP6u6xvbAkMenfe4Mi872CqNkJzgcnQL837e8NdeDxBgrWQRTluKq5Lqdhfg=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-retry": ["@smithy/util-retry@4.2.6", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-x7CeDQLPQ9cb6xN7fRJEjlP9NyGW/YeXWc4j/RUhg4I+H60F0PEeRc2c/z3rm9zmsdiMFzpV/rT+4UHW6KM1SA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - "@aws-sdk/client-cognito-identity/@aws-sdk/core": ["@aws-sdk/core@3.623.0", "", { "dependencies": { "@smithy/core": "^2.3.2", "@smithy/node-config-provider": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/signature-v4": "^4.1.0", "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-8Toq3X6trX/67obSdh4K0MFQY4f132bEbr1i0YPDWk/O3KdBt12mLC/sW3aVRnlIs110XMuX9yrWWqJ8fDW10g=="], "@aws-sdk/client-cognito-identity/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.623.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.620.1", "@aws-sdk/credential-provider-http": "3.622.0", "@aws-sdk/credential-provider-ini": "3.623.0", "@aws-sdk/credential-provider-process": "3.620.1", "@aws-sdk/credential-provider-sso": "3.623.0", "@aws-sdk/credential-provider-web-identity": "3.621.0", "@aws-sdk/types": "3.609.0", "@smithy/credential-provider-imds": "^3.2.0", "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-qDwCOkhbu5PfaQHyuQ+h57HEx3+eFhKdtIw7aISziWkGdFrMe07yIBd7TJqGe4nxXnRF1pfkg05xeOlMId997g=="], @@ -4971,8 +5199,12 @@ "@aws-sdk/client-kendra/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], + "@aws-sdk/client-kendra/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + "@aws-sdk/client-kendra/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], + "@aws-sdk/client-kendra/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="], + "@aws-sdk/client-kendra/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], "@aws-sdk/client-kendra/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], @@ -4987,11 +5219,13 @@ "@aws-sdk/client-kendra/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="], + "@aws-sdk/client-kendra/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@aws-sdk/client-kendra/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="], "@aws-sdk/client-kendra/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - "@aws-sdk/client-s3/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="], + "@aws-sdk/client-kendra/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], "@aws-sdk/client-sso/@aws-sdk/core": ["@aws-sdk/core@3.623.0", "", { "dependencies": { "@smithy/core": "^2.3.2", "@smithy/node-config-provider": "^3.1.4", "@smithy/protocol-http": "^4.1.0", "@smithy/signature-v4": "^4.1.0", "@smithy/smithy-client": "^3.1.12", "@smithy/types": "^3.3.0", "@smithy/util-middleware": "^3.0.3", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-8Toq3X6trX/67obSdh4K0MFQY4f132bEbr1i0YPDWk/O3KdBt12mLC/sW3aVRnlIs110XMuX9yrWWqJ8fDW10g=="], @@ -5135,7 +5369,7 @@ "@aws-sdk/client-sso-oidc/@smithy/util-utf8": ["@smithy/util-utf8@3.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA=="], - "@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="], + "@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="], "@aws-sdk/credential-provider-cognito-identity/@aws-sdk/types": ["@aws-sdk/types@3.609.0", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q=="], @@ -5165,33 +5399,23 @@ "@aws-sdk/credential-provider-ini/@smithy/types": ["@smithy/types@3.3.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="], + "@aws-sdk/credential-provider-login/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="], - "@aws-sdk/credential-provider-login/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.16", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-HrdtnadvTGAQUr18sPzGlE5El3ICphnH6SU7UQOMOWFgRKbTRNN8msTxM4emzguUso9CzaHU2xy5ctSrmK5YNA=="], - "@aws-sdk/credential-provider-login/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.18", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/types": "^3.973.5", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/node-http-handler": "^4.4.14", "@smithy/property-provider": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.2", "@smithy/types": "^4.13.0", "@smithy/util-stream": "^4.5.17", "tslib": "^2.6.2" } }, "sha512-NyB6smuZAixND5jZumkpkunQ0voc4Mwgkd+SZ6cvAzIB7gK8HV8Zd4rS8Kn5MmoGgusyNfVGG+RLoYc4yFiw+A=="], - "@aws-sdk/credential-provider-login/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="], + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.17", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/credential-provider-env": "^3.972.16", "@aws-sdk/credential-provider-http": "^3.972.18", "@aws-sdk/credential-provider-login": "^3.972.17", "@aws-sdk/credential-provider-process": "^3.972.16", "@aws-sdk/credential-provider-sso": "^3.972.17", "@aws-sdk/credential-provider-web-identity": "^3.972.17", "@aws-sdk/nested-clients": "^3.996.7", "@aws-sdk/types": "^3.973.5", "@smithy/credential-provider-imds": "^4.2.11", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-dFqh7nfX43B8dO1aPQHOcjC0SnCJ83H3F+1LoCh3X1P7E7N09I+0/taID0asU6GCddfDExqnEvQtDdkuMe5tKQ=="], - "@aws-sdk/credential-provider-login/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="], + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.16", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-n89ibATwnLEg0ZdZmUds5bq8AfBAdoYEDpqP3uzPLaRuGelsKlIvCYSNNvfgGLi8NaHPNNhs1HjJZYbqkW9b+g=="], - "@aws-sdk/credential-provider-login/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.17", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/nested-clients": "^3.996.7", "@aws-sdk/token-providers": "3.1004.0", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-wGtte+48xnhnhHMl/MsxzacBPs5A+7JJedjiP452IkHY7vsbYKcvQBqFye8LwdTJVeHtBHv+JFeTscnwepoWGg=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-N27eFoRrO6MeUNumtNHDW9WOiwfd59LPXPqDrIa3kWL/s+fOKFHb9xIcF++bAwtcZnAxKkgpDCUP+INNZskE+w=="], + "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.17", "", { "dependencies": { "@aws-sdk/core": "^3.973.18", "@aws-sdk/nested-clients": "^3.996.7", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-8aiVJh6fTdl8gcyL+sVNcNwTtWpmoFa1Sh7xlj6Z7L/cZ/tYMEBHq44wTYG8Kt0z/PpGNopD89nbj3FHl9QmTA=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/property-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-stream": "^4.1.2", "tslib": "^2.6.2" } }, "sha512-Xt9/U8qUCiw1hihztWkNeIR+arg6P+yda10OuCHX6kFVx3auTlU7+hCqs3UxqniGU4dguHuftf3mRpi5/GJ33Q=="], + "@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.11", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.11", "@smithy/property-provider": "^4.2.11", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/credential-provider-env": "3.758.0", "@aws-sdk/credential-provider-http": "3.758.0", "@aws-sdk/credential-provider-process": "3.758.0", "@aws-sdk/credential-provider-sso": "3.758.0", "@aws-sdk/credential-provider-web-identity": "3.758.0", "@aws-sdk/nested-clients": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/credential-provider-imds": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-cymSKMcP5d+OsgetoIZ5QCe1wnp2Q/tq+uIxVdh9MbfdBBEnl9Ecq6dH6VlYS89sp4QKuxHxkWXVnbXU3Q19Aw=="], - - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-AzcY74QTPqcbXWVgjpPZ3HOmxQZYPROIBz2YINF0OQk0MhezDWV/O7Xec+K1+MPGQO3qS6EDrUUlnPLjsqieHA=="], - - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.758.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.758.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/token-providers": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-x0FYJqcOLUCv8GLLFDYMXRAQKGjoM+L0BG4BiHYZRDf24yQWFCAZsCQAYKo6XZYh2qznbsW6f//qpyJ5b0QVKQ=="], - - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/nested-clients": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-XGguXhBqiCXMXRxcfCAVPlMbm3VyJTou79r/3mxWddHWF0XbhaQiBIbUz6vobVTD25YQRbWSmSch7VA8kI5Lrw=="], - - "@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.0.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg=="], - - "@aws-sdk/credential-provider-node/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="], + "@aws-sdk/credential-provider-node/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="], "@aws-sdk/credential-provider-process/@aws-sdk/types": ["@aws-sdk/types@3.609.0", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q=="], @@ -5217,113 +5441,41 @@ "@aws-sdk/credential-providers/@smithy/types": ["@smithy/types@3.3.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA=="], - "@aws-sdk/eventstream-handler-node/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + "@aws-sdk/middleware-websocket/@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.972.7", "", { "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/querystring-builder": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-V+PbnWfUl93GuFwsOHsAq7hY/fnm9kElRqR8IexIJr5Rvif9e614X5sGSyz3mVSf1YAZ+VTy63W1/pGdA55zyA=="], - "@aws-sdk/eventstream-handler-node/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.758.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw=="], - "@aws-sdk/middleware-eventstream/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/types": ["@aws-sdk/types@3.734.0", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg=="], - "@aws-sdk/middleware-eventstream/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.0.6", "", { "dependencies": { "@smithy/core": "^3.1.5", "@smithy/middleware-serde": "^4.0.2", "@smithy/node-config-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-middleware": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-ftpmkTHIFqgaFugcjzLZv3kzPEFsBFSnq1JsIkr2mwFzCraZVhQk2gqN51OOeRxqhbPTkRFj39Qd2V91E/mQxg=="], - "@aws-sdk/middleware-eventstream/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], + "@aws-sdk/s3-request-presigner/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], - "@aws-sdk/middleware-websocket/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client": ["@smithy/smithy-client@4.1.6", "", { "dependencies": { "@smithy/core": "^3.1.5", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-stack": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-stream": "^4.1.2", "tslib": "^2.6.2" } }, "sha512-UYDolNg6h2O0L+cJjtgSyKKvEKCOa/8FHYJnBobyeoeWDmNpXjwOAtw16ezyeu1ETuuLEOZbrynK0ZY1Lx9Jbw=="], - "@aws-sdk/middleware-websocket/@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/querystring-builder": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-MS5eSEtDUFIAMHrJaMERiHAvDPdfxc/T869ZjDNFAIiZhyc037REw0aoTNeimNXDNy2txRNZJaAUn/kE4RwN+g=="], + "@aws-sdk/s3-request-presigner/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="], - "@aws-sdk/middleware-websocket/@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.6", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-6OiaAaEbLB6dEkRbQyNzFSJv5HDvly3Mc6q/qcPd2uS/g3szR8wAIkh7UndAFKfMypNSTuZ6eCBmgCLR5LacTg=="], + "@aws-sdk/token-providers/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="], - "@aws-sdk/middleware-websocket/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="], - - "@aws-sdk/middleware-websocket/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="], - - "@aws-sdk/middleware-websocket/@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="], - - "@aws-sdk/middleware-websocket/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], - - "@aws-sdk/nested-clients/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="], - - "@aws-sdk/nested-clients/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-tAaObaAnsP1XnLGndfkGWFuzrJYuk9W0b/nLvol66t8FZExIAf/WdkT2NNAWOYxljVs++oHnyHBCxIlaHrzSiw=="], - - "@aws-sdk/nested-clients/@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-aPSJ12d3a3Ea5nyEnLbijCaaYJT2QjQ9iW+zGh5QcZYXmOGWbKVyPSxmVOboZQG+c1M8t6d2O7tqrwzIq8L8qw=="], - - "@aws-sdk/nested-clients/@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.948.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-Qa8Zj+EAqA0VlAVvxpRnpBpIWJI9KUwaioY1vkeNVwXPlNaz9y9zCKVM9iU9OZ5HXpoUg6TnhATAHXHAE8+QsQ=="], - - "@aws-sdk/nested-clients/@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@smithy/core": "^3.18.7", "@smithy/protocol-http": "^5.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-7rpKV8YNgCP2R4F9RjWZFcD2R+SO/0R4VHIbY9iZJdH2MzzJ8ZG7h8dZ2m8QkQd1fjx4wrFJGGPJUTYXPV3baA=="], - - "@aws-sdk/nested-clients/@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/config-resolver": "^4.4.3", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-wOKhzzWsshXGduxO4pqSiNyL9oUtk4BEvjWm9aaq6Hmfdoydq6v6t0rAGHWPjFwy9z2haovGRi3C8IxdMB4muw=="], - - "@aws-sdk/nested-clients/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], - - "@aws-sdk/nested-clients/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-endpoints": "^3.2.5", "tslib": "^2.6.2" } }, "sha512-0Zx3Ntdpu+z9Wlm7JKUBOzS9EunwKAb4KdGUQQxDqh5Lc3ta5uBoub+FgmVuzwnmBu9U1Os8UuwVTH0Lgu+P5w=="], - - "@aws-sdk/nested-clients/@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.936.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@smithy/types": "^4.9.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-eZ/XF6NxMtu+iCma58GRNRxSq4lHo6zHQLOZRIeL/ghqYJirqHdenMOwrzPettj60KWlv827RVebP9oNVrwZbw=="], - - "@aws-sdk/nested-clients/@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.947.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/node-config-provider": "^4.3.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-+vhHoDrdbb+zerV4noQk1DHaUMNzWFWPpPYjVTwW2186k5BEJIecAMChYkghRrBVJ3KPWP1+JnZwOd72F3d4rQ=="], - - "@aws-sdk/nested-clients/@smithy/config-resolver": ["@smithy/config-resolver@4.4.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/types": "^4.10.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-s3U5ChS21DwU54kMmZ0UJumoS5cg0+rGVZvN6f5Lp6EbAVi0ZyP+qDSHdewfmXKUgNK1j3z45JyzulkDukrjAA=="], - - "@aws-sdk/nested-clients/@smithy/core": ["@smithy/core@3.19.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.7", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-stream": "^4.5.7", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Y9oHXpBcXQgYHOcAEmxjkDilUbSTkgKjoHYed3WaYUH8jngq8lPWDBSpjHblJ9uOgBdy5mh3pzebrScDdYr29w=="], - - "@aws-sdk/nested-clients/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="], - - "@aws-sdk/nested-clients/@smithy/hash-node": ["@smithy/hash-node@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-k3Dy9VNR37wfMh2/1RHkFf/e0rMyN0pjY0FdyY6ItJRjENYyVPRMwad6ZR1S9HFm6tTuIOd9pqKBmtJ4VHxvxg=="], - - "@aws-sdk/nested-clients/@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-E4t/V/q2T46RY21fpfznd1iSLTvCXKNKo4zJ1QuEFN4SE9gKfu2vb6bgq35LpufkQ+SETWIC7ZAf2GGvTlBaMQ=="], - - "@aws-sdk/nested-clients/@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.6", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-0cjqjyfj+Gls30ntq45SsBtqF3dfJQCeqQPyGz58Pk8OgrAr5YiB7ZvDzjCA94p4r6DCI4qLm7FKobqBjf515w=="], - - "@aws-sdk/nested-clients/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.0", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-serde": "^4.2.7", "@smithy/node-config-provider": "^4.3.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-M6qWfUNny6NFNy8amrCGIb9TfOMUkHVtg9bHtEFGRgfH7A7AtPpn/fcrToGPjVDK1ECuMVvqGQOXcZxmu9K+7A=="], - - "@aws-sdk/nested-clients/@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.16", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/protocol-http": "^5.3.6", "@smithy/service-error-classification": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-retry": "^4.2.6", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-XPpNhNRzm3vhYm7YCsyw3AtmWggJbg1wNGAoqb7NBYr5XA5isMRv14jgbYyUV6IvbTBFZQdf2QpeW43LrRdStQ=="], - - "@aws-sdk/nested-clients/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="], - - "@aws-sdk/nested-clients/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-JSbALU3G+JS4kyBZPqnJ3hxIYwOVRV7r9GNQMS6j5VsQDo5+Es5nddLfr9TQlxZLNHPvKSh+XSB0OuWGfSWFcA=="], - - "@aws-sdk/nested-clients/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-fYEyL59Qe82Ha1p97YQTMEQPJYmBS+ux76foqluaTVWoG9Px5J53w6NvXZNE3wP7lIicLDF7Vj1Em18XTX7fsA=="], - - "@aws-sdk/nested-clients/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="], - - "@aws-sdk/nested-clients/@smithy/smithy-client": ["@smithy/smithy-client@4.10.1", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-endpoint": "^4.4.0", "@smithy/middleware-stack": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-stream": "^4.5.7", "tslib": "^2.6.2" } }, "sha512-1ovWdxzYprhq+mWqiGZlt3kF69LJthuQcfY9BIyHx9MywTFKzFapluku1QXoaBB43GCsLDxNqS+1v30ure69AA=="], - - "@aws-sdk/nested-clients/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], - - "@aws-sdk/nested-clients/@smithy/url-parser": ["@smithy/url-parser@4.2.6", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tVoyzJ2vXp4R3/aeV4EQjBDmCuWxRa8eo3KybL7Xv4wEM16nObYh7H1sNfcuLWHAAAzb0RVyxUz1S3sGj4X+Tg=="], - - "@aws-sdk/nested-clients/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], - - "@aws-sdk/nested-clients/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], - - "@aws-sdk/nested-clients/@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA=="], - - "@aws-sdk/nested-clients/@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.15", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-LiZQVAg/oO8kueX4c+oMls5njaD2cRLXRfcjlTYjhIqmwHnCwkQO5B3dMQH0c5PACILxGAQf6Mxsq7CjlDc76A=="], - - "@aws-sdk/nested-clients/@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.18", "", { "dependencies": { "@smithy/config-resolver": "^4.4.4", "@smithy/credential-provider-imds": "^4.2.6", "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/smithy-client": "^4.10.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-Kw2J+KzYm9C9Z9nY6+W0tEnoZOofstVCMTshli9jhQbQCy64rueGfKzPfuFBnVUqZD9JobxTh2DzHmPkp/Va/Q=="], - - "@aws-sdk/nested-clients/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-v60VNM2+mPvgHCBXEfMCYrQ0RepP6u6xvbAkMenfe4Mi872CqNkJzgcnQL837e8NdeDxBgrWQRTluKq5Lqdhfg=="], - - "@aws-sdk/nested-clients/@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="], - - "@aws-sdk/nested-clients/@smithy/util-retry": ["@smithy/util-retry@4.2.6", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-x7CeDQLPQ9cb6xN7fRJEjlP9NyGW/YeXWc4j/RUhg4I+H60F0PEeRc2c/z3rm9zmsdiMFzpV/rT+4UHW6KM1SA=="], - - "@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - - "@aws-sdk/token-providers/@aws-sdk/core": ["@aws-sdk/core@3.947.0", "", { "dependencies": { "@aws-sdk/types": "3.936.0", "@aws-sdk/xml-builder": "3.930.0", "@smithy/core": "^3.18.7", "@smithy/node-config-provider": "^4.3.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/signature-v4": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Khq4zHhuAkvCFuFbgcy3GrZTzfSX7ZIjIcW1zRDxXRLZKRtuhnZdonqTUfaWi5K42/4OmxkYNpsO7X7trQOeHw=="], - - "@aws-sdk/token-providers/@aws-sdk/types": ["@aws-sdk/types@3.936.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-uz0/VlMd2pP5MepdrHizd+T+OKfyK4r3OA9JI+L/lPKg0YFQosdJNCKisr6o70E3dh8iMpFYxF1UN/4uZsyARg=="], - - "@aws-sdk/token-providers/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], - - "@aws-sdk/token-providers/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="], - - "@aws-sdk/token-providers/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], + "@aws-sdk/util-format-url/@aws-sdk/types": ["@aws-sdk/types@3.734.0", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg=="], "@aws-sdk/util-format-url/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], + "@aws-sdk/util-format-url/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="], + "@azure/core-http-compat/@azure/abort-controller": ["@azure/abort-controller@1.1.0", "", { "dependencies": { "tslib": "^2.2.0" } }, "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw=="], - "@azure/core-xml/fast-xml-parser": ["fast-xml-parser@5.0.9", "", { "dependencies": { "strnum": "^2.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-2mBwCiuW3ycKQQ6SOesSB8WeF+fIGb6I/GG5vU5/XEptwFFhp9PE8b9O7fbs2dpq9fXn4ULR3UsfydNUCntf5A=="], + "@azure/storage-blob/@azure/core-http-compat": ["@azure/core-http-compat@2.3.2", "", { "dependencies": { "@azure/abort-controller": "^2.1.2" }, "peerDependencies": { "@azure/core-client": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0" } }, "sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw=="], + + "@azure/storage-blob/@azure/core-paging": ["@azure/core-paging@1.6.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA=="], + + "@azure/storage-blob/@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="], + + "@azure/storage-common/@azure/core-http-compat": ["@azure/core-http-compat@2.3.2", "", { "dependencies": { "@azure/abort-controller": "^2.1.2" }, "peerDependencies": { "@azure/core-client": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0" } }, "sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw=="], + + "@azure/storage-common/@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="], + + "@babel/core/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], "@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], @@ -5331,11 +5483,17 @@ "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], - "@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], + "@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.25.9", "", { "dependencies": { "@babel/types": "^7.25.9" } }, "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g=="], - "@babel/plugin-transform-classes/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], + "@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - "@babel/plugin-transform-explicit-resource-management/@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="], + "@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], + + "@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], + + "@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], + + "@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.8", "", { "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.5.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg=="], @@ -5343,18 +5501,40 @@ "@babel/plugin-transform-runtime/babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.5.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.5.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg=="], - "@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.23.10", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw=="], + "@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - "@babel/preset-typescript/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.23.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA=="], + "@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], + + "@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], + + "@babel/traverse/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], "@codesandbox/sandpack-client/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "@csstools/css-color-parser/@csstools/color-helpers": ["@csstools/color-helpers@4.0.0", "", {}, "sha512-wjyXB22/h2OvxAr3jldPB7R7kjTUEzopvjitS8jWtyd8fN6xJ8vy1HnHu0ZNfEkqpBJgQ76Q+sBDshWcMvTa/w=="], + "@csstools/postcss-cascade-layers/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "@csstools/postcss-is-pseudo-class/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "@csstools/postcss-scope-pseudo-class/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "@csstools/selector-resolve-nested/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "@csstools/selector-specificity/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], + "@eslint/config-array/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "@eslint/config-array/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + "@google/genai/google-auth-library": ["google-auth-library@10.5.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" } }, "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w=="], + + "@grpc/grpc-js/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@grpc/proto-loader/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="], + "@headlessui/react/@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.12", "", { "dependencies": { "@tanstack/virtual-core": "3.13.12" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA=="], "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], @@ -5373,39 +5553,31 @@ "@istanbuljs/load-nyc-config/resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], - "@jest/console/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "@jest/console/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "@jest/console/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "@jest/core/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "@jest/core/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "@jest/core/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], "@jest/core/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "@jest/environment/@jest/fake-timers": ["@jest/fake-timers@30.2.0", "", { "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" } }, "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw=="], + "@jest/environment/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], - "@jest/environment/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="], - - "@jest/environment-jsdom-abstract/@jest/fake-timers": ["@jest/fake-timers@30.2.0", "", { "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" } }, "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw=="], - - "@jest/environment-jsdom-abstract/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="], - - "@jest/environment-jsdom-abstract/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "@jest/environment-jsdom-abstract/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "@jest/expect/expect": ["expect@30.2.0", "", { "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" } }, "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw=="], - "@jest/fake-timers/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="], + "@jest/fake-timers/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], - "@jest/fake-timers/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], - - "@jest/globals/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="], + "@jest/pattern/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "@jest/reporters/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@jest/reporters/glob": ["glob@10.5.0", "", { "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": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + "@jest/reporters/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], - "@jest/reporters/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "@jest/reporters/glob": ["glob@10.5.0", "", { "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": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], "@jest/reporters/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], @@ -5415,10 +5587,10 @@ "@jest/transform/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@jest/transform/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], - "@jest/transform/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + "@jest/types/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + "@jridgewell/gen-mapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], "@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], @@ -5445,38 +5617,30 @@ "@langchain/mistralai/uuid": ["uuid@10.0.0", "", { "bin": "dist/bin/uuid" }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="], - "@librechat/client/@babel/preset-env": ["@babel/preset-env@7.28.5", "", { "dependencies": { "@babel/compat-data": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", "@babel/plugin-transform-block-scoping": "^7.28.5", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", "@babel/plugin-transform-classes": "^7.28.4", "@babel/plugin-transform-computed-properties": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", "@babel/plugin-transform-exponentiation-operator": "^7.28.5", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", "@babel/plugin-transform-object-rest-spread": "^7.28.4", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", "@babel/plugin-transform-regenerator": "^7.28.4", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-spread": "^7.27.1", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", "@babel/plugin-transform-unicode-property-regex": "^7.27.1", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg=="], - - "@librechat/client/@babel/preset-react": ["@babel/preset-react@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ=="], - - "@librechat/client/@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="], - - "@librechat/frontend/@babel/preset-env": ["@babel/preset-env@7.28.5", "", { "dependencies": { "@babel/compat-data": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", "@babel/plugin-transform-block-scoping": "^7.28.5", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", "@babel/plugin-transform-classes": "^7.28.4", "@babel/plugin-transform-computed-properties": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", "@babel/plugin-transform-exponentiation-operator": "^7.28.5", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", "@babel/plugin-transform-object-rest-spread": "^7.28.4", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", "@babel/plugin-transform-regenerator": "^7.28.4", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-spread": "^7.27.1", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", "@babel/plugin-transform-unicode-property-regex": "^7.27.1", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg=="], - - "@librechat/frontend/@babel/preset-react": ["@babel/preset-react@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ=="], - - "@librechat/frontend/@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="], + "@librechat/client/rollup": ["rollup@4.37.0", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.37.0", "@rollup/rollup-android-arm64": "4.37.0", "@rollup/rollup-darwin-arm64": "4.37.0", "@rollup/rollup-darwin-x64": "4.37.0", "@rollup/rollup-freebsd-arm64": "4.37.0", "@rollup/rollup-freebsd-x64": "4.37.0", "@rollup/rollup-linux-arm-gnueabihf": "4.37.0", "@rollup/rollup-linux-arm-musleabihf": "4.37.0", "@rollup/rollup-linux-arm64-gnu": "4.37.0", "@rollup/rollup-linux-arm64-musl": "4.37.0", "@rollup/rollup-linux-loongarch64-gnu": "4.37.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.37.0", "@rollup/rollup-linux-riscv64-gnu": "4.37.0", "@rollup/rollup-linux-riscv64-musl": "4.37.0", "@rollup/rollup-linux-s390x-gnu": "4.37.0", "@rollup/rollup-linux-x64-gnu": "4.37.0", "@rollup/rollup-linux-x64-musl": "4.37.0", "@rollup/rollup-win32-arm64-msvc": "4.37.0", "@rollup/rollup-win32-ia32-msvc": "4.37.0", "@rollup/rollup-win32-x64-msvc": "4.37.0", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg=="], "@librechat/frontend/@react-spring/web": ["@react-spring/web@9.7.5", "", { "dependencies": { "@react-spring/animated": "~9.7.5", "@react-spring/core": "~9.7.5", "@react-spring/shared": "~9.7.5", "@react-spring/types": "~9.7.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ=="], "@librechat/frontend/@testing-library/jest-dom": ["@testing-library/jest-dom@5.17.0", "", { "dependencies": { "@adobe/css-tools": "^4.0.1", "@babel/runtime": "^7.9.2", "@types/testing-library__jest-dom": "^5.9.1", "aria-query": "^5.0.0", "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.5.6", "lodash": "^4.17.15", "redent": "^3.0.0" } }, "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg=="], - "@librechat/frontend/framer-motion": ["framer-motion@11.18.2", "", { "dependencies": { "motion-dom": "^11.18.1", "motion-utils": "^11.18.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid"] }, "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w=="], + "@librechat/frontend/dompurify": ["dompurify@3.3.0", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ=="], - "@librechat/frontend/jest-environment-jsdom": ["jest-environment-jsdom@29.7.0", "", { "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/jsdom": "^20.0.0", "@types/node": "*", "jest-mock": "^29.7.0", "jest-util": "^29.7.0", "jsdom": "^20.0.0" }, "peerDependencies": { "canvas": "^2.5.0" }, "optionalPeers": ["canvas"] }, "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA=="], + "@librechat/frontend/framer-motion": ["framer-motion@11.18.2", "", { "dependencies": { "motion-dom": "^11.18.1", "motion-utils": "^11.18.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid"] }, "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w=="], "@librechat/frontend/lucide-react": ["lucide-react@0.394.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0" } }, "sha512-PzTbJ0bsyXRhH59k5qe7MpTd5MxlpYZUcM9kGSwvPGAfnn0J6FElDwu2EX6Vuh//F7y60rcVJiFQ7EK9DCMgfw=="], "@mcp-ui/client/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.21.0", "", { "dependencies": { "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" } }, "sha512-YFBsXJMFCyI1zP98u7gezMFKX4lgu/XpoZJk7ufI6UlFKXLj2hAMUuRlQX/nrmIPOmhRrG6tw2OQ2ZA/ZlXYpQ=="], + "@mistralai/mistralai/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="], + "@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], - "@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@7.5.0", "", { "peerDependencies": { "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg=="], - - "@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], - "@node-saml/node-saml/@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], + "@node-saml/node-saml/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "@node-saml/node-saml/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], + "@node-saml/passport-saml/@types/express": ["@types/express@4.17.23", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ=="], "@node-saml/passport-saml/passport": ["passport@0.7.0", "", { "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", "utils-merge": "^1.0.1" } }, "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ=="], @@ -5485,9 +5649,19 @@ "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-logs": "0.208.0", "@opentelemetry/sdk-metrics": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ=="], + "@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="], + "@opentelemetry/sdk-node/@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.207.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/otlp-exporter-base": "0.207.0", "@opentelemetry/otlp-transformer": "0.207.0", "@opentelemetry/resources": "2.2.0", "@opentelemetry/sdk-trace-base": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-HSRBzXHIC7C8UfPQdu15zEEoBGv0yWkhEwxqgPCHVUKUQ9NLHVGXkVrf65Uaj7UwmAkC1gQfkuVYvLlD//AnUQ=="], - "@radix-ui/react-alert-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + "@radix-ui/react-alert-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA=="], + + "@radix-ui/react-alert-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="], "@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g=="], @@ -5503,11 +5677,29 @@ "@radix-ui/react-collapsible/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA=="], - "@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + "@radix-ui/react-dialog/@radix-ui/primitive": ["@radix-ui/primitive@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA=="], - "@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], + "@radix-ui/react-dialog/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="], - "@radix-ui/react-dismissable-layer/@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], + "@radix-ui/react-dialog/@radix-ui/react-context": ["@radix-ui/react-context@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg=="], + + "@radix-ui/react-dialog/@radix-ui/react-id": ["@radix-ui/react-id@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-layout-effect": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw=="], + + "@radix-ui/react-dialog/@radix-ui/react-presence": ["@radix-ui/react-presence@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0", "@radix-ui/react-use-layout-effect": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w=="], + + "@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA=="], + + "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="], + + "@radix-ui/react-dialog/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/primitive": ["@radix-ui/primitive@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg=="], "@radix-ui/react-dropdown-menu/@radix-ui/primitive": ["@radix-ui/primitive@1.1.0", "", {}, "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="], @@ -5521,6 +5713,12 @@ "@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="], + "@radix-ui/react-focus-scope/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="], + + "@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA=="], + + "@radix-ui/react-focus-scope/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg=="], + "@radix-ui/react-hover-card/@radix-ui/primitive": ["@radix-ui/primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw=="], "@radix-ui/react-hover-card/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="], @@ -5567,8 +5765,6 @@ "@radix-ui/react-menu/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="], - "@radix-ui/react-menu/react-remove-scroll": ["react-remove-scroll@2.5.5", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.3", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", "use-sidecar": "^1.1.2" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw=="], - "@radix-ui/react-popover/@radix-ui/primitive": ["@radix-ui/primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" } }, "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw=="], "@radix-ui/react-popover/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="], @@ -5591,8 +5787,6 @@ "@radix-ui/react-popover/@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA=="], - "@radix-ui/react-popover/react-remove-scroll": ["react-remove-scroll@2.5.5", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.3", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", "use-sidecar": "^1.1.2" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw=="], - "@radix-ui/react-popper/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="], "@radix-ui/react-popper/@radix-ui/react-context": ["@radix-ui/react-context@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg=="], @@ -5603,6 +5797,8 @@ "@radix-ui/react-popper/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ=="], + "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-slot": "1.0.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA=="], + "@radix-ui/react-presence/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="], "@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ=="], @@ -5621,10 +5817,16 @@ "@radix-ui/react-select/@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA=="], + "@radix-ui/react-select/@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + "@radix-ui/react-select/@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.7", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ=="], + "@radix-ui/react-select/@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], + "@radix-ui/react-select/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], + "@radix-ui/react-select/react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA=="], + "@radix-ui/react-slider/@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], "@radix-ui/react-slider/@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], @@ -5669,6 +5871,8 @@ "@radix-ui/react-toast/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-primitive": "1.0.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA=="], + "@radix-ui/react-use-escape-keydown/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg=="], + "@radix-ui/react-use-size/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ=="], "@rollup/plugin-babel/@rollup/pluginutils": ["@rollup/pluginutils@3.1.0", "", { "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", "picomatch": "^2.2.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0" } }, "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg=="], @@ -5681,45 +5885,21 @@ "@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "@smithy/abort-controller/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], - "@smithy/credential-provider-imds/@smithy/node-config-provider": ["@smithy/node-config-provider@3.1.4", "", { "dependencies": { "@smithy/property-provider": "^3.1.3", "@smithy/shared-ini-file-loader": "^3.1.4", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ=="], "@smithy/credential-provider-imds/@smithy/types": ["@smithy/types@3.3.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA=="], "@smithy/credential-provider-imds/@smithy/url-parser": ["@smithy/url-parser@3.0.3", "", { "dependencies": { "@smithy/querystring-parser": "^3.0.3", "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A=="], - "@smithy/eventstream-codec/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], - - "@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="], - - "@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], - - "@smithy/middleware-retry/uuid": ["uuid@9.0.1", "", { "bin": "dist/bin/uuid" }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], - - "@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="], - - "@smithy/node-http-handler/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="], - - "@smithy/node-http-handler/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], + "@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="], "@smithy/property-provider/@smithy/types": ["@smithy/types@3.3.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA=="], - "@smithy/querystring-builder/@smithy/types": ["@smithy/types@4.10.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-K9mY7V/f3Ul+/Gz4LJANZ3vJ/yiBIwCyxe0sPT4vNJK63Srvd+Yk1IzP0t+nE7XFSpIGtzR71yljtnqpUTYFlQ=="], + "@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="], - "@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@smithy/util-defaults-mode-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.11", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.11", "@smithy/property-provider": "^4.2.11", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "tslib": "^2.6.2" } }, "sha512-lBXrS6ku0kTj3xLmsJW0WwqWbGQ6ueooYyp/1L9lkyT0M02C+DWwYwc5aTyXFbRaK38ojALxNixg+LxKSHZc0g=="], - "@smithy/signature-v4/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - - "@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="], - - "@smithy/util-defaults-mode-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.0.1", "", { "dependencies": { "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "tslib": "^2.6.2" } }, "sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg=="], - - "@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="], - - "@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="], - - "@smithy/util-waiter/@smithy/abort-controller": ["@smithy/abort-controller@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA=="], + "@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/property-provider@4.2.11", "", { "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" } }, "sha512-14T1V64o6/ndyrnl1ze1ZhyLzIeYNN47oF/QU6P5m82AEtyOkMJTb0gO1dPubYjyyKuPD6OSVMPDKe+zioOnCg=="], "@surma/rollup-plugin-off-main-thread/magic-string": ["magic-string@0.25.9", "", { "dependencies": { "sourcemap-codec": "^1.4.8" } }, "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="], @@ -5731,18 +5911,48 @@ "@testing-library/dom/pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + "@types/body-parser/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@types/connect/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@types/express-serve-static-core/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@types/jsdom/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@types/jsonwebtoken/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@types/ldapjs/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + "@types/mdast/@types/unist": ["@types/unist@2.0.10", "", {}, "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="], + "@types/node-fetch/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@types/send/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@types/serve-static/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + "@types/testing-library__jest-dom/@types/jest": ["@types/jest@29.5.12", "", { "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw=="], "@types/winston/winston": ["winston@3.11.0", "", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.4.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.5.0" } }, "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g=="], + "@types/xml-encryption/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@types/xml2js/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@typescript-eslint/parser/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "@typescript-eslint/type-utils/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "@typescript-eslint/typescript-estree/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "@typescript-eslint/typescript-estree/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="], + "@vitejs/plugin-react/@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], + "accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], "ajv-formats/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], @@ -5751,10 +5961,18 @@ "asn1.js/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="], + "autoprefixer/fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], + "babel-jest/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], "babel-plugin-root-import/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + "body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + + "body-parser/qs": ["qs@6.15.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ=="], + + "body-parser/raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + "browser-resolve/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], "browserify-rsa/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="], @@ -5763,12 +5981,16 @@ "browserify-sign/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "bun-types/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + "caniuse-api/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "cheerio/undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "cli-truncate/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], @@ -5783,32 +6005,66 @@ "cookie-parser/cookie-signature": ["cookie-signature@1.0.6", "", {}, "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="], + "core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], + "create-ecdh/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="], + "css-blank-pseudo/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "css-has-pseudo/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + "cssnano/lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="], "cssnano/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], + "cytoscape-fcose/cose-base": ["cose-base@2.2.0", "", { "dependencies": { "layout-base": "^2.0.0" } }, "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g=="], + + "d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], + + "d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="], + + "d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="], + "data-urls/whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], "diffie-hellman/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="], - "domexception/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "error-ex/is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], + "eslint/@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], + + "eslint/ajv": ["ajv@6.12.6", "", { "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" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], + + "eslint/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "eslint/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], "eslint-import-resolver-node/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], + "eslint-import-resolver-typescript/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + "eslint-plugin-i18next/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="], + "eslint-plugin-import/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "eslint-plugin-jsx-a11y/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "eslint-plugin-react/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="], "expect/jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="], + "expect/jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + "express-session/cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="], "express-session/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -5819,25 +6075,23 @@ "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], + "finalhandler/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "flat-cache/keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], - "gaxios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + "gaxios/https-proxy-agent": ["https-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.0.2", "debug": "4" } }, "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA=="], - "glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], + "gcp-metadata/gaxios": ["gaxios@5.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^5.0.0", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA=="], - "google-auth-library/gaxios": ["gaxios@6.2.0", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ=="], + "glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], "google-auth-library/gcp-metadata": ["gcp-metadata@6.1.1", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="], - "googleapis-common/gaxios": ["gaxios@6.2.0", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ=="], + "happy-dom/whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="], - "googleapis-common/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], - - "googleapis-common/uuid": ["uuid@9.0.1", "", { "bin": "dist/bin/uuid" }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], - - "gtoken/gaxios": ["gaxios@6.2.0", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "is-stream": "^2.0.0", "node-fetch": "^2.6.9" } }, "sha512-H6+bHeoEAU5D6XNc6mPKeN5dLZqEDs9Gpk6I+SZBEzK5So58JVrHPmevNi35fRl1J9Y5TaeLW0kYx3pCJ1U2mQ=="], + "happy-dom/ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], "hast-util-from-html/@types/hast": ["@types/hast@2.3.10", "", { "dependencies": { "@types/unist": "^2" } }, "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw=="], @@ -5871,10 +6125,16 @@ "http-proxy-agent/agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="], + "http-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "https-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "import-from/resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], "import-in-the-middle/cjs-module-lexer": ["cjs-module-lexer@1.2.3", "", {}, "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ=="], + "ioredis/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "is-bun-module/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], "istanbul-lib-instrument/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], @@ -5885,43 +6145,37 @@ "istanbul-lib-source-maps/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "istanbul-lib-source-maps/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "jake/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "jest-changed-files/execa": ["execa@5.1.1", "", { "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" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], - "jest-changed-files/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "jest-circus/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "jest-circus/jest-matcher-utils": ["jest-matcher-utils@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", "jest-diff": "30.2.0", "pretty-format": "30.2.0" } }, "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg=="], - "jest-circus/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], - "jest-circus/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], "jest-circus/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "jest-cli/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], - "jest-config/glob": ["glob@10.5.0", "", { "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": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], - "jest-config/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], - "jest-config/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], "jest-config/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], "jest-diff/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], - "jest-each/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], - "jest-each/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], - "jest-environment-node/@jest/fake-timers": ["@jest/fake-timers@30.2.0", "", { "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" } }, "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw=="], + "jest-environment-jsdom/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], - "jest-environment-node/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="], - - "jest-environment-node/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "jest-environment-node/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "jest-file-loader/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "jest-haste-map/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "jest-haste-map/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "jest-leak-detector/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], @@ -5931,24 +6185,18 @@ "jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "jest-mock/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="], - - "jest-resolve/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "jest-mock/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "jest-resolve/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - "jest-runner/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "jest-runner/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "jest-runner/source-map-support": ["source-map-support@0.5.13", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w=="], - "jest-runtime/@jest/fake-timers": ["@jest/fake-timers@30.2.0", "", { "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" } }, "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw=="], + "jest-runtime/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "jest-runtime/glob": ["glob@10.5.0", "", { "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": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], - "jest-runtime/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="], - - "jest-runtime/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], - "jest-runtime/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], "jest-runtime/strip-bom": ["strip-bom@4.0.0", "", {}, "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="], @@ -5959,28 +6207,24 @@ "jest-snapshot/jest-matcher-utils": ["jest-matcher-utils@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", "jest-diff": "30.2.0", "pretty-format": "30.2.0" } }, "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg=="], - "jest-snapshot/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], - "jest-snapshot/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], "jest-snapshot/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], "jest-snapshot/synckit": ["synckit@0.11.11", "", { "dependencies": { "@pkgr/core": "^0.2.9" } }, "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw=="], - "jest-util/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="], - - "jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], - - "jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "jest-util/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "jest-validate/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], - "jest-watcher/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "jest-watcher/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], - "jest-worker/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "jest-worker/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], + "js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "jsdom/decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], "jsdom/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], @@ -5991,30 +6235,28 @@ "jsonwebtoken/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "jszip/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "jwks-rsa/@types/express": ["@types/express@4.17.21", "", { "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", "@types/serve-static": "*" } }, "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ=="], + "jwks-rsa/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "jwks-rsa/jose": ["jose@4.15.5", "", {}, "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg=="], "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], "keyv-file/@keyv/serialize": ["@keyv/serialize@1.0.3", "", { "dependencies": { "buffer": "^6.0.3" } }, "sha512-qnEovoOp5Np2JDGonIDL6Ayihw0RhnRh6vxPuHo4RDn1UOzwEo4AeIfpL6UGIrsceWrCMiVPgwRjbHu4vYFc3g=="], - "keyv-file/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], - "langsmith/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], "langsmith/uuid": ["uuid@10.0.0", "", { "bin": "dist/bin/uuid" }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="], "ldapauth-fork/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], - "librechat-data-provider/@babel/preset-env": ["@babel/preset-env@7.28.5", "", { "dependencies": { "@babel/compat-data": "^7.28.5", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.27.1", "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.27.1", "@babel/plugin-transform-async-generator-functions": "^7.28.0", "@babel/plugin-transform-async-to-generator": "^7.27.1", "@babel/plugin-transform-block-scoped-functions": "^7.27.1", "@babel/plugin-transform-block-scoping": "^7.28.5", "@babel/plugin-transform-class-properties": "^7.27.1", "@babel/plugin-transform-class-static-block": "^7.28.3", "@babel/plugin-transform-classes": "^7.28.4", "@babel/plugin-transform-computed-properties": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.5", "@babel/plugin-transform-dotall-regex": "^7.27.1", "@babel/plugin-transform-duplicate-keys": "^7.27.1", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-dynamic-import": "^7.27.1", "@babel/plugin-transform-explicit-resource-management": "^7.28.0", "@babel/plugin-transform-exponentiation-operator": "^7.28.5", "@babel/plugin-transform-export-namespace-from": "^7.27.1", "@babel/plugin-transform-for-of": "^7.27.1", "@babel/plugin-transform-function-name": "^7.27.1", "@babel/plugin-transform-json-strings": "^7.27.1", "@babel/plugin-transform-literals": "^7.27.1", "@babel/plugin-transform-logical-assignment-operators": "^7.28.5", "@babel/plugin-transform-member-expression-literals": "^7.27.1", "@babel/plugin-transform-modules-amd": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-modules-systemjs": "^7.28.5", "@babel/plugin-transform-modules-umd": "^7.27.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", "@babel/plugin-transform-new-target": "^7.27.1", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-numeric-separator": "^7.27.1", "@babel/plugin-transform-object-rest-spread": "^7.28.4", "@babel/plugin-transform-object-super": "^7.27.1", "@babel/plugin-transform-optional-catch-binding": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.28.5", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/plugin-transform-private-methods": "^7.27.1", "@babel/plugin-transform-private-property-in-object": "^7.27.1", "@babel/plugin-transform-property-literals": "^7.27.1", "@babel/plugin-transform-regenerator": "^7.28.4", "@babel/plugin-transform-regexp-modifiers": "^7.27.1", "@babel/plugin-transform-reserved-words": "^7.27.1", "@babel/plugin-transform-shorthand-properties": "^7.27.1", "@babel/plugin-transform-spread": "^7.27.1", "@babel/plugin-transform-sticky-regex": "^7.27.1", "@babel/plugin-transform-template-literals": "^7.27.1", "@babel/plugin-transform-typeof-symbol": "^7.27.1", "@babel/plugin-transform-unicode-escapes": "^7.27.1", "@babel/plugin-transform-unicode-property-regex": "^7.27.1", "@babel/plugin-transform-unicode-regex": "^7.27.1", "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg=="], - - "librechat-data-provider/@babel/preset-react": ["@babel/preset-react@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-transform-react-display-name": "^7.28.0", "@babel/plugin-transform-react-jsx": "^7.27.1", "@babel/plugin-transform-react-jsx-development": "^7.27.1", "@babel/plugin-transform-react-pure-annotations": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ=="], - - "librechat-data-provider/@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="], - "lint-staged/chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + "lint-staged/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "log-update/ansi-escapes": ["ansi-escapes@7.0.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw=="], "log-update/slice-ansi": ["slice-ansi@7.1.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg=="], @@ -6027,30 +6269,48 @@ "lru-memoizer/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], - "mathjs/fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], - "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], "mdast-util-math/unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], "mdast-util-mdx-jsx/unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], + "memorystore/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "mermaid/marked": ["marked@16.4.2", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA=="], + + "mermaid/uuid": ["uuid@11.1.0", "", { "bin": "dist/esm/bin/uuid" }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + + "micromark/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "miller-rabin/bn.js": ["bn.js@4.12.2", "", {}, "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw=="], + "mlly/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "monaco-editor/dompurify": ["dompurify@3.2.7", "", { "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw=="], + "mongodb-connection-string-url/whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], + "mongodb-memory-server-core/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "mongodb-memory-server-core/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], - "multer/mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": "bin/cmd.js" }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], + "mquery/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], "multer/type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + "new-find-package-json/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "node-stdlib-browser/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "node-stdlib-browser/pkg-dir": ["pkg-dir@5.0.0", "", { "dependencies": { "find-up": "^5.0.0" } }, "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA=="], + "nodemon/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "nodemon/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "nodemon/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], @@ -6073,18 +6333,28 @@ "playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], + "postcss/nanoid": ["nanoid@3.3.8", "", { "bin": "bin/nanoid.cjs" }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], + + "postcss-attribute-case-insensitive/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + "postcss-colormin/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], "postcss-convert-values/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], + "postcss-custom-selectors/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "postcss-dir-pseudo-class/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "postcss-focus-visible/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + + "postcss-focus-within/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + "postcss-import/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], "postcss-load-config/lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="], "postcss-load-config/yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], - "postcss-loader/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], - "postcss-merge-rules/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], "postcss-minify-params/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], @@ -6093,14 +6363,20 @@ "postcss-modules-scope/postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="], + "postcss-nesting/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + "postcss-normalize-unicode/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], - "postcss-preset-env/autoprefixer": ["autoprefixer@10.4.17", "", { "dependencies": { "browserslist": "^4.22.2", "caniuse-lite": "^1.0.30001578", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": "bin/autoprefixer" }, "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg=="], + "postcss-preset-env/autoprefixer": ["autoprefixer@10.4.27", "", { "dependencies": { "browserslist": "^4.28.1", "caniuse-lite": "^1.0.30001774", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA=="], - "postcss-preset-env/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], + "postcss-preset-env/browserslist": ["browserslist@4.28.1", "", { "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" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + + "postcss-pseudo-class-any-link/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], "postcss-reduce-initial/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], + "postcss-selector-not/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], + "pretty-format/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], "pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -6133,10 +6409,14 @@ "remark-supersub/unist-util-visit": ["unist-util-visit@4.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.1.1" } }, "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg=="], + "require-in-the-middle/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "resolve-cwd/resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + "rollup/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "rollup-plugin-postcss/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], "rollup-plugin-typescript2/@rollup/pluginutils": ["@rollup/pluginutils@4.2.1", "", { "dependencies": { "estree-walker": "^2.0.1", "picomatch": "^2.2.2" } }, "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ=="], @@ -6147,7 +6427,9 @@ "rollup-pluginutils/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="], - "schema-utils/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "router/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "send/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], "sharp/semver": ["semver@7.7.3", "", { "bin": "bin/semver.js" }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], @@ -6173,6 +6455,8 @@ "sucrase/glob": ["glob@10.5.0", "", { "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": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + "superagent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "superagent/mime": ["mime@2.6.0", "", { "bin": "cli.js" }, "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="], "superagent/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], @@ -6181,6 +6465,10 @@ "svgo/css-select": ["css-select@4.3.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ=="], + "svgo/sax": ["sax@1.5.0", "", {}, "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA=="], + + "swr/use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + "tailwindcss/arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], "tailwindcss/lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="], @@ -6193,17 +6481,9 @@ "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - "terser-webpack-plugin/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - - "terser-webpack-plugin/jest-worker": ["jest-worker@27.5.1", "", { "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" } }, "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg=="], - "test-exclude/glob": ["glob@7.2.3", "", { "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" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "tinyglobby/fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="], - - "tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], - - "ts-node/diff": ["diff@4.0.2", "", {}, "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="], + "test-exclude/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": "lib/cli.js" }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], @@ -6225,19 +6505,17 @@ "vasync/verror": ["verror@1.10.0", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw=="], + "verror/core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="], + "vfile-location/@types/unist": ["@types/unist@2.0.10", "", {}, "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="], "vfile-location/vfile": ["vfile@5.3.7", "", { "dependencies": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", "unist-util-stringify-position": "^3.0.0", "vfile-message": "^3.0.0" } }, "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g=="], - "webpack/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], - - "webpack/eslint-scope": ["eslint-scope@5.1.1", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="], - - "webpack/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "vite/postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], "winston-daily-rotate-file/winston-transport": ["winston-transport@4.7.0", "", { "dependencies": { "logform": "^2.3.2", "readable-stream": "^3.6.0", "triple-beam": "^1.3.0" } }, "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg=="], - "workbox-build/@babel/preset-env": ["@babel/preset-env@7.23.9", "", { "dependencies": { "@babel/compat-data": "^7.23.5", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-option": "^7.23.5", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", "@babel/plugin-syntax-import-assertions": "^7.23.3", "@babel/plugin-syntax-import-attributes": "^7.23.3", "@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", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.23.3", "@babel/plugin-transform-async-generator-functions": "^7.23.9", "@babel/plugin-transform-async-to-generator": "^7.23.3", "@babel/plugin-transform-block-scoped-functions": "^7.23.3", "@babel/plugin-transform-block-scoping": "^7.23.4", "@babel/plugin-transform-class-properties": "^7.23.3", "@babel/plugin-transform-class-static-block": "^7.23.4", "@babel/plugin-transform-classes": "^7.23.8", "@babel/plugin-transform-computed-properties": "^7.23.3", "@babel/plugin-transform-destructuring": "^7.23.3", "@babel/plugin-transform-dotall-regex": "^7.23.3", "@babel/plugin-transform-duplicate-keys": "^7.23.3", "@babel/plugin-transform-dynamic-import": "^7.23.4", "@babel/plugin-transform-exponentiation-operator": "^7.23.3", "@babel/plugin-transform-export-namespace-from": "^7.23.4", "@babel/plugin-transform-for-of": "^7.23.6", "@babel/plugin-transform-function-name": "^7.23.3", "@babel/plugin-transform-json-strings": "^7.23.4", "@babel/plugin-transform-literals": "^7.23.3", "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", "@babel/plugin-transform-member-expression-literals": "^7.23.3", "@babel/plugin-transform-modules-amd": "^7.23.3", "@babel/plugin-transform-modules-commonjs": "^7.23.3", "@babel/plugin-transform-modules-systemjs": "^7.23.9", "@babel/plugin-transform-modules-umd": "^7.23.3", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", "@babel/plugin-transform-new-target": "^7.23.3", "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", "@babel/plugin-transform-numeric-separator": "^7.23.4", "@babel/plugin-transform-object-rest-spread": "^7.23.4", "@babel/plugin-transform-object-super": "^7.23.3", "@babel/plugin-transform-optional-catch-binding": "^7.23.4", "@babel/plugin-transform-optional-chaining": "^7.23.4", "@babel/plugin-transform-parameters": "^7.23.3", "@babel/plugin-transform-private-methods": "^7.23.3", "@babel/plugin-transform-private-property-in-object": "^7.23.4", "@babel/plugin-transform-property-literals": "^7.23.3", "@babel/plugin-transform-regenerator": "^7.23.3", "@babel/plugin-transform-reserved-words": "^7.23.3", "@babel/plugin-transform-shorthand-properties": "^7.23.3", "@babel/plugin-transform-spread": "^7.23.3", "@babel/plugin-transform-sticky-regex": "^7.23.3", "@babel/plugin-transform-template-literals": "^7.23.3", "@babel/plugin-transform-typeof-symbol": "^7.23.3", "@babel/plugin-transform-unicode-escapes": "^7.23.3", "@babel/plugin-transform-unicode-property-regex": "^7.23.3", "@babel/plugin-transform-unicode-regex": "^7.23.3", "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.8", "babel-plugin-polyfill-corejs3": "^0.9.0", "babel-plugin-polyfill-regenerator": "^0.5.5", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A=="], + "workbox-build/@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], "workbox-build/@rollup/plugin-replace": ["@rollup/plugin-replace@2.4.2", "", { "dependencies": { "@rollup/pluginutils": "^3.1.0", "magic-string": "^0.25.7" }, "peerDependencies": { "rollup": "^1.20.0 || ^2.0.0" } }, "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg=="], @@ -6245,7 +6523,7 @@ "workbox-build/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], - "workbox-build/glob": ["glob@7.2.3", "", { "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" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "workbox-build/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="], "workbox-build/pretty-bytes": ["pretty-bytes@5.6.0", "", {}, "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg=="], @@ -6293,6 +6571,8 @@ "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="], + "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-bAllBpmaWINpf0brXQWh/hjkBctapknZPYb3FJRlBHytEGHi7TpgqBXi8riT0tc6RVWChhnw58rQz22acOmBuw=="], "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-jEvb8C7tuRBFhe8vZY9vm9z6UQnbP85IMEt3Qiz0dxAd341Hgu0lOzMv5mSKQ5yBnTLq+t3FPKgD9tIiHLqxSQ=="], @@ -6317,6 +6597,12 @@ "@aws-sdk/client-bedrock-agent-runtime/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + + "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ=="], + + "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], "@aws-sdk/client-bedrock-agent-runtime/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], @@ -6325,6 +6611,8 @@ "@aws-sdk/client-bedrock-agent-runtime/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/middleware-retry/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="], "@aws-sdk/client-bedrock-agent-runtime/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-y5ozxeQ9omVjbnJo9dtTsdXj9BEvGx2X8xvRgKnV+/7wLBuYJQL6dOa/qMY6omyHi7yjt1OA97jZLoVRYi8lxA=="], @@ -6349,62 +6637,6 @@ "@aws-sdk/client-bedrock-agent-runtime/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-VR2V6dRELmzwAsCpK4GqxUi6UW5WNhAXS9F9AzWi5jvijwJo3nH92YNJUP4quMpgFZxJHEWyXLWgPjh9u0zYOA=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/node-http-handler": "^4.4.5", "@smithy/property-provider": "^4.2.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/util-stream": "^4.5.6", "tslib": "^2.6.2" } }, "sha512-inF09lh9SlHj63Vmr5d+LmwPXZc2IbK8lAruhOr3KLsZAIHEgHgGPXWDC2ukTEMzg0pkexQ6FOhXXad6klK4RA=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/credential-provider-env": "3.947.0", "@aws-sdk/credential-provider-http": "3.947.0", "@aws-sdk/credential-provider-login": "3.952.0", "@aws-sdk/credential-provider-process": "3.947.0", "@aws-sdk/credential-provider-sso": "3.952.0", "@aws-sdk/credential-provider-web-identity": "3.952.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/credential-provider-imds": "^4.2.5", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-N5B15SwzMkZ8/LLopNksTlPEWWZn5tbafZAUfMY5Xde4rSHGWmv5H/ws2M3P8L0X77E2wKnOJsNmu+GsArBreQ=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.947.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-WpanFbHe08SP1hAJNeDdBDVz9SGgMu/gc0XJ9u3uNpW99nKZjDpvPRAdW7WLA4K6essMjxWkguIGNOpij6Do2Q=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.952.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.948.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/token-providers": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-1CQdP5RzxeXuEfytbAD5TgreY1c9OacjtCdO8+n9m05tpzBABoNBof0hcjzw1dtrWFH7deyUgfwCl1TAN3yBWQ=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.952.0", "", { "dependencies": { "@aws-sdk/core": "3.947.0", "@aws-sdk/nested-clients": "3.952.0", "@aws-sdk/types": "3.936.0", "@smithy/property-provider": "^4.2.5", "@smithy/shared-ini-file-loader": "^4.4.0", "@smithy/types": "^4.9.0", "tslib": "^2.6.2" } }, "sha512-5hJbfaZdHDAP8JlwplNbXJAat9Vv7L0AbTZzkbPIgjHhC3vrMf5r3a6I1HWFp5i5pXo7J45xyuf5uQGZJxJlCg=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-xBmawExyTzOjbhzkZwg+vVm/khg28kG+rj2sbGlULjFd1jI70sv/cbpaR0Ev4Yfd6CpDUDRMe64cTqR//wAOyA=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/config-resolver/@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.6", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-olIfZ230B64TvPD6b0tPvrEp2eB0FkyL3KvDlqF4RVmIc/kn3orzXnV6DTQdOOW5UU+M5zKY3/BU47X420/oPw=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.6", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-olIfZ230B64TvPD6b0tPvrEp2eB0FkyL3KvDlqF4RVmIc/kn3orzXnV6DTQdOOW5UU+M5zKY3/BU47X420/oPw=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/middleware-endpoint/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0" } }, "sha512-Q73XBrzJlGTut2nf5RglSntHKgAG0+KiTJdO5QQblLfr4TdliGwIAha1iZIjwisc3rA5ulzqwwsYC6xrclxVQg=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-YmWxl32SQRw/kIRccSOxzS/Ib8/b5/f9ex0r5PR40jRJg8X1wgM3KrR2In+8zvOGVhRSXgvyQpw9yOSlmfmSnA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-defaults-mode-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-xBmawExyTzOjbhzkZwg+vVm/khg28kG+rj2sbGlULjFd1jI70sv/cbpaR0Ev4Yfd6CpDUDRMe64cTqR//wAOyA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0" } }, "sha512-Q73XBrzJlGTut2nf5RglSntHKgAG0+KiTJdO5QQblLfr4TdliGwIAha1iZIjwisc3rA5ulzqwwsYC6xrclxVQg=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - "@aws-sdk/client-cognito-identity/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@4.1.0", "", { "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-hex-encoding": "^3.0.0", "@smithy/util-middleware": "^3.0.3", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag=="], "@aws-sdk/client-cognito-identity/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@3.1.4", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ=="], @@ -6443,6 +6675,8 @@ "@aws-sdk/client-kendra/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="], + "@aws-sdk/client-kendra/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + "@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-bAllBpmaWINpf0brXQWh/hjkBctapknZPYb3FJRlBHytEGHi7TpgqBXi8riT0tc6RVWChhnw58rQz22acOmBuw=="], "@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-jEvb8C7tuRBFhe8vZY9vm9z6UQnbP85IMEt3Qiz0dxAd341Hgu0lOzMv5mSKQ5yBnTLq+t3FPKgD9tIiHLqxSQ=="], @@ -6499,10 +6733,6 @@ "@aws-sdk/client-kendra/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - "@aws-sdk/client-s3/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="], - - "@aws-sdk/client-s3/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], - "@aws-sdk/client-sso-oidc/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@4.1.0", "", { "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "@smithy/protocol-http": "^4.1.0", "@smithy/types": "^3.3.0", "@smithy/util-hex-encoding": "^3.0.0", "@smithy/util-middleware": "^3.0.3", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag=="], "@aws-sdk/client-sso-oidc/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@3.1.4", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ=="], @@ -6589,104 +6819,46 @@ "@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-utf8": ["@smithy/util-utf8@3.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], - - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core": ["@smithy/core@3.19.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.7", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-stream": "^4.5.7", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Y9oHXpBcXQgYHOcAEmxjkDilUbSTkgKjoHYed3WaYUH8jngq8lPWDBSpjHblJ9uOgBdy5mh3pzebrScDdYr29w=="], - - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-fYEyL59Qe82Ha1p97YQTMEQPJYmBS+ux76foqluaTVWoG9Px5J53w6NvXZNE3wP7lIicLDF7Vj1Em18XTX7fsA=="], - - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="], - - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.10.1", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-endpoint": "^4.4.0", "@smithy/middleware-stack": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-stream": "^4.5.7", "tslib": "^2.6.2" } }, "sha512-1ovWdxzYprhq+mWqiGZlt3kF69LJthuQcfY9BIyHx9MywTFKzFapluku1QXoaBB43GCsLDxNqS+1v30ure69AA=="], - - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], - - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="], - - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="], - - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.758.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/middleware-host-header": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.734.0", "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", "@aws-sdk/util-user-agent-node": "3.758.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/hash-node": "^4.0.1", "@smithy/invalid-dependency": "^4.0.1", "@smithy/middleware-content-length": "^4.0.1", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-retry": "^4.0.7", "@smithy/middleware-serde": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.7", "@smithy/util-defaults-mode-node": "^4.0.7", "@smithy/util-endpoints": "^3.0.1", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg=="], - - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.758.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/middleware-host-header": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.734.0", "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", "@aws-sdk/util-user-agent-node": "3.758.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/hash-node": "^4.0.1", "@smithy/invalid-dependency": "^4.0.1", "@smithy/middleware-content-length": "^4.0.1", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-retry": "^4.0.7", "@smithy/middleware-serde": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.7", "@smithy/util-defaults-mode-node": "^4.0.7", "@smithy/util-endpoints": "^3.0.1", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-BoGO6IIWrLyLxQG6txJw6RT2urmbtlwfggapNCrNPyYjlXpzTSJhBYjndg7TpDATFd0SXL0zm8y/tXsUXNkdYQ=="], - - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.758.0", "", { "dependencies": { "@aws-sdk/nested-clients": "3.758.0", "@aws-sdk/types": "3.734.0", "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-ckptN1tNrIfQUaGWm/ayW1ddG+imbKN7HHhjFdS4VfItsP0QQOB0+Ov+tpgb4MoNR4JaUghMIVStjIeHN2ks1w=="], - - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.758.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/middleware-host-header": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.734.0", "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", "@aws-sdk/util-user-agent-node": "3.758.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/hash-node": "^4.0.1", "@smithy/invalid-dependency": "^4.0.1", "@smithy/middleware-content-length": "^4.0.1", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-retry": "^4.0.7", "@smithy/middleware-serde": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.7", "@smithy/util-defaults-mode-node": "^4.0.7", "@smithy/util-endpoints": "^3.0.1", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg=="], - "@aws-sdk/credential-providers/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@3.1.4", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ=="], - "@aws-sdk/middleware-websocket/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.6", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-olIfZ230B64TvPD6b0tPvrEp2eB0FkyL3KvDlqF4RVmIc/kn3orzXnV6DTQdOOW5UU+M5zKY3/BU47X420/oPw=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.758.0", "", { "dependencies": { "@aws-sdk/core": "3.758.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-arn-parser": "3.723.0", "@smithy/core": "^3.1.5", "@smithy/node-config-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A=="], - "@aws-sdk/middleware-websocket/@smithy/fetch-http-handler/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], - "@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core": ["@smithy/core@3.1.5", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.2", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA=="], - "@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.2", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ=="], - "@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/node-config-provider": ["@smithy/node-config-provider@4.0.1", "", { "dependencies": { "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ=="], - "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw=="], - "@aws-sdk/nested-clients/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.0.1", "", { "dependencies": { "@smithy/querystring-parser": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g=="], - "@aws-sdk/nested-clients/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], - "@aws-sdk/nested-clients/@smithy/config-resolver/@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core": ["@smithy/core@3.1.5", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.2", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA=="], - "@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA=="], - "@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.1.2", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw=="], - "@aws-sdk/nested-clients/@smithy/middleware-endpoint/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="], + "@aws-sdk/util-format-url/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], - "@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0" } }, "sha512-Q73XBrzJlGTut2nf5RglSntHKgAG0+KiTJdO5QQblLfr4TdliGwIAha1iZIjwisc3rA5ulzqwwsYC6xrclxVQg=="], - - "@aws-sdk/nested-clients/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], - - "@aws-sdk/nested-clients/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.1", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tph+oQYPbpN6NamF030hx1gb5YN2Plog+GLaRHpoEDwp8+ZPG26rIJvStG9hkWzN2HBn3HcWg0sHeB0tmkYzqA=="], - - "@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="], - - "@aws-sdk/nested-clients/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-YmWxl32SQRw/kIRccSOxzS/Ib8/b5/f9ex0r5PR40jRJg8X1wgM3KrR2In+8zvOGVhRSXgvyQpw9yOSlmfmSnA=="], - - "@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - - "@aws-sdk/nested-clients/@smithy/util-defaults-mode-browser/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], - - "@aws-sdk/nested-clients/@smithy/util-defaults-mode-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.6", "@smithy/property-provider": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-xBmawExyTzOjbhzkZwg+vVm/khg28kG+rj2sbGlULjFd1jI70sv/cbpaR0Ev4Yfd6CpDUDRMe64cTqR//wAOyA=="], - - "@aws-sdk/nested-clients/@smithy/util-defaults-mode-node/@smithy/property-provider": ["@smithy/property-provider@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-a/tGSLPtaia2krbRdwR4xbZKO8lU67DjMk/jfY4QKt4PRlKML+2tL/gmAuhNdFDioO6wOq0sXkfnddNFH9mNUA=="], - - "@aws-sdk/nested-clients/@smithy/util-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0" } }, "sha512-Q73XBrzJlGTut2nf5RglSntHKgAG0+KiTJdO5QQblLfr4TdliGwIAha1iZIjwisc3rA5ulzqwwsYC6xrclxVQg=="], - - "@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core": ["@smithy/core@3.19.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.7", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-stream": "^4.5.7", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-Y9oHXpBcXQgYHOcAEmxjkDilUbSTkgKjoHYed3WaYUH8jngq8lPWDBSpjHblJ9uOgBdy5mh3pzebrScDdYr29w=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.6", "", { "dependencies": { "@smithy/property-provider": "^4.2.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-fYEyL59Qe82Ha1p97YQTMEQPJYmBS+ux76foqluaTVWoG9Px5J53w6NvXZNE3wP7lIicLDF7Vj1Em18XTX7fsA=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qLRZzP2+PqhE3OSwvY2jpBbP0WKTZ9opTsn+6IWYI0SKVpbG+imcfNxXPq9fj5XeaUTr7odpsNpK6dmoiM1gJQ=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.6", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.6", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-P1TXDHuQMadTMTOBv4oElZMURU4uyEhxhHfn+qOc2iofW9Rd4sZtBGx58Lzk112rIGVEYZT8eUMK4NftpewpRA=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.10.1", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-endpoint": "^4.4.0", "@smithy/middleware-stack": "^4.2.6", "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "@smithy/util-stream": "^4.5.7", "tslib": "^2.6.2" } }, "sha512-1ovWdxzYprhq+mWqiGZlt3kF69LJthuQcfY9BIyHx9MywTFKzFapluku1QXoaBB43GCsLDxNqS+1v30ure69AA=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-qrvXUkxBSAFomM3/OEMuDVwjh4wtqK8D2uDZPShzIqOylPst6gor2Cdp6+XrH4dyksAWq/bE2aSDYBTTnj0Rxg=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - - "@azure/core-xml/fast-xml-parser/strnum": ["strnum@2.0.5", "", {}, "sha512-YAT3K/sgpCUxhxNMrrdhtod3jckkpYwH6JAuwmUdXZsmzH1wUyzTMrrK2wYCEEqlKwrWDd35NeuUkbBy/1iK+Q=="], + "@babel/helper-compilation-targets/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], "@babel/helper-compilation-targets/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], + + "@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], + + "@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], + + "@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], + "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="], "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="], @@ -6695,13 +6867,21 @@ "@babel/plugin-transform-runtime/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="], - "@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="], + "@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - "@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="], + "@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - "@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="], + "@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - "@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="], + "@google/genai/google-auth-library/gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="], + + "@google/genai/google-auth-library/gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="], + + "@google/genai/google-auth-library/gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="], + + "@grpc/grpc-js/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@grpc/proto-loader/protobufjs/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "@headlessui/react/@tanstack/react-virtual/@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.12", "", {}, "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA=="], @@ -6711,36 +6891,34 @@ "@istanbuljs/load-nyc-config/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - "@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "@jest/console/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@jest/core/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "@jest/core/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - "@jest/environment-jsdom-abstract/@jest/fake-timers/@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="], + "@jest/environment-jsdom-abstract/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "@jest/environment/@jest/fake-timers/@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="], - - "@jest/environment/@jest/fake-timers/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], - - "@jest/environment/jest-mock/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "@jest/environment/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "@jest/expect/expect/@jest/expect-utils": ["@jest/expect-utils@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0" } }, "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA=="], "@jest/expect/expect/jest-matcher-utils": ["jest-matcher-utils@30.2.0", "", { "dependencies": { "@jest/get-type": "30.1.0", "chalk": "^4.1.2", "jest-diff": "30.2.0", "pretty-format": "30.2.0" } }, "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg=="], - "@jest/expect/expect/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="], + "@jest/fake-timers/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "@jest/expect/expect/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "@jest/pattern/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "@jest/fake-timers/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], - - "@jest/fake-timers/jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], - - "@jest/globals/jest-mock/jest-util": ["jest-util@30.2.0", "", { "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" } }, "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA=="], + "@jest/reporters/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "@jest/reporters/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "@jest/reporters/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "@jest/reporters/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "@jest/types/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core": ["@aws-sdk/core@3.927.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@aws-sdk/xml-builder": "3.921.0", "@smithy/core": "^3.17.2", "@smithy/node-config-provider": "^4.3.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/signature-v4": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-QOtR9QdjNeC7bId3fc/6MnqoEezvQ2Fk+x6F+Auf7NhOxwYAtB1nvh0k3+gJHWVGpfxN1I8keahRZd79U68/ag=="], "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/eventstream-handler-node": ["@aws-sdk/eventstream-handler-node@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-DTKHeH1Bk17zSdoa5qXPGwCmZXuhQReqXOVW2/jIVX8NGVvnraH7WppGPlQxBjFtwSSwVTgzH2NVPgediQphNA=="], @@ -6773,6 +6951,12 @@ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/core": ["@smithy/core@3.17.2", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-stream": "^4.5.5", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-n3g4Nl1Te+qGPDbNFAYf+smkRVB+JhFsGy9uJXXZQEufoP4u0r+WLh6KvTDolCswaagysDc/afS1yvb2jnj1gQ=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-d5T7ZS3J/r8P/PDjgmCcutmNxnSRvPH1U6iHeXjzI50sMr78GLmFcrczLw33Ap92oEKqa4CLrkAPeSSOqvGdUA=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-lxfDT0UuSc1HqltOGsTEAlZ6H29gpfDSdEPTapD5G63RbnYToZ+ezjzdonCCH90j5tRRCw3aLXVbiZaBW3VRVg=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-TPhiGByWnYyzcpU/K3pO5V7QgtXYpE0NaJPEZBCa1Y5jlw5SjqzMSbFiLb+ZkJhqoQc0ImGyVINqnq1ze0ZRcQ=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="], "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/hash-node": ["@smithy/hash-node@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kKU0gVhx/ppVMntvUOZE7WRMFW86HuaxLwvqileBEjL7PoILI8/djoILw3gPQloGVE6O0oOzqafxeNi2KbnUJw=="], @@ -6793,8 +6977,12 @@ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], @@ -6809,12 +6997,16 @@ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="], "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/property-provider": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-bAllBpmaWINpf0brXQWh/hjkBctapknZPYb3FJRlBHytEGHi7TpgqBXi8riT0tc6RVWChhnw58rQz22acOmBuw=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.927.0", "", { "dependencies": { "@aws-sdk/core": "3.927.0", "@aws-sdk/types": "3.922.0", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-jEvb8C7tuRBFhe8vZY9vm9z6UQnbP85IMEt3Qiz0dxAd341Hgu0lOzMv5mSKQ5yBnTLq+t3FPKgD9tIiHLqxSQ=="], @@ -6835,275 +7027,49 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-y5ozxeQ9omVjbnJo9dtTsdXj9BEvGx2X8xvRgKnV+/7wLBuYJQL6dOa/qMY6omyHi7yjt1OA97jZLoVRYi8lxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@smithy/types": ["@smithy/types@4.8.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA=="], + "@langchain/google-gauth/google-auth-library/gaxios": ["gaxios@7.1.3", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" } }, "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ=="], "@langchain/google-gauth/google-auth-library/gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="], "@langchain/google-gauth/google-auth-library/gtoken": ["gtoken@8.0.0", "", { "dependencies": { "gaxios": "^7.0.0", "jws": "^4.0.0" } }, "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw=="], - "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q=="], + "@librechat/client/rollup/@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.37.0", "", { "os": "android", "cpu": "arm" }, "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ=="], - "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA=="], + "@librechat/client/rollup/@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.37.0", "", { "os": "android", "cpu": "arm64" }, "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA=="], + "@librechat/client/rollup/@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.37.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw=="], + "@librechat/client/rollup/@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.37.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ=="], - "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw=="], + "@librechat/client/rollup/@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.37.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg=="], + "@librechat/client/rollup/@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.37.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="], + "@librechat/client/rollup/@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.37.0", "", { "os": "linux", "cpu": "arm" }, "sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1", "@babel/traverse": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q=="], + "@librechat/client/rollup/@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.37.0", "", { "os": "linux", "cpu": "arm" }, "sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA=="], + "@librechat/client/rollup/@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.37.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg=="], + "@librechat/client/rollup/@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.37.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g=="], + "@librechat/client/rollup/@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA=="], + "@librechat/client/rollup/@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.37.0", "", { "os": "linux", "cpu": "none" }, "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg=="], + "@librechat/client/rollup/@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.37.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA=="], + "@librechat/client/rollup/@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.37.0", "", { "os": "linux", "cpu": "x64" }, "sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/template": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw=="], + "@librechat/client/rollup/@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.37.0", "", { "os": "linux", "cpu": "x64" }, "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="], + "@librechat/client/rollup/@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.37.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw=="], + "@librechat/client/rollup/@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.37.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.28.5", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.4", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="], - - "@librechat/client/@babel/preset-env/core-js-compat": ["core-js-compat@3.47.0", "", { "dependencies": { "browserslist": "^4.28.0" } }, "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ=="], - - "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA=="], - - "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/types": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw=="], - - "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="], - - "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA=="], - - "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="], - - "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1", "@babel/traverse": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/template": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.28.5", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.4", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="], - - "@librechat/frontend/@babel/preset-env/core-js-compat": ["core-js-compat@3.47.0", "", { "dependencies": { "browserslist": "^4.28.0" } }, "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ=="], - - "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA=="], - - "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/types": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw=="], - - "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="], - - "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA=="], - - "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="], - - "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA=="], + "@librechat/client/rollup/@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.37.0", "", { "os": "win32", "cpu": "x64" }, "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA=="], "@librechat/frontend/@react-spring/web/@react-spring/animated": ["@react-spring/animated@9.7.5", "", { "dependencies": { "@react-spring/shared": "~9.7.5", "@react-spring/types": "~9.7.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg=="], @@ -7121,22 +7087,20 @@ "@librechat/frontend/@testing-library/jest-dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + "@librechat/frontend/@testing-library/jest-dom/lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "@librechat/frontend/framer-motion/motion-dom": ["motion-dom@11.18.1", "", { "dependencies": { "motion-utils": "^11.18.1" } }, "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw=="], "@librechat/frontend/framer-motion/motion-utils": ["motion-utils@11.18.1", "", {}, "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA=="], - "@librechat/frontend/jest-environment-jsdom/@jest/environment": ["@jest/environment@29.7.0", "", { "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0" } }, "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw=="], - - "@librechat/frontend/jest-environment-jsdom/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="], - - "@librechat/frontend/jest-environment-jsdom/@types/jsdom": ["@types/jsdom@20.0.1", "", { "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", "parse5": "^7.0.0" } }, "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom": ["jsdom@20.0.3", "", { "dependencies": { "abab": "^2.0.6", "acorn": "^8.8.1", "acorn-globals": "^7.0.0", "cssom": "^0.5.0", "cssstyle": "^2.3.0", "data-urls": "^3.0.2", "decimal.js": "^10.4.2", "domexception": "^4.0.0", "escodegen": "^2.0.0", "form-data": "^4.0.0", "html-encoding-sniffer": "^3.0.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.1", "is-potential-custom-element-name": "^1.0.1", "nwsapi": "^2.2.2", "parse5": "^7.1.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^4.1.2", "w3c-xmlserializer": "^4.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^2.0.0", "whatwg-mimetype": "^3.0.0", "whatwg-url": "^11.0.0", "ws": "^8.11.0", "xml-name-validator": "^4.0.0" }, "peerDependencies": { "canvas": "^2.5.0" }, "optionalPeers": ["canvas"] }, "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ=="], - "@mcp-ui/client/@modelcontextprotocol/sdk/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "@mcp-ui/client/@modelcontextprotocol/sdk/express": ["express@5.1.0", "", { "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" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], + "@mcp-ui/client/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@7.5.0", "", { "peerDependencies": { "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg=="], + "@mcp-ui/client/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="], + "@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "@node-saml/passport-saml/@types/express/@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.6", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A=="], @@ -7147,18 +7111,32 @@ "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer/@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.208.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.208.0", "@opentelemetry/core": "2.2.0", "@opentelemetry/resources": "2.2.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA=="], + "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer/protobufjs": ["protobufjs@7.4.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw=="], + + "@opentelemetry/otlp-transformer/protobufjs/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + "@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg=="], "@radix-ui/react-checkbox/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg=="], "@radix-ui/react-checkbox/@radix-ui/react-use-controllable-state/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="], + "@radix-ui/react-dialog/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ=="], + + "@radix-ui/react-dialog/@radix-ui/react-presence/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ=="], + + "@radix-ui/react-dialog/@radix-ui/react-use-controllable-state/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg=="], + + "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="], + "@radix-ui/react-dropdown-menu/@radix-ui/react-id/@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="], "@radix-ui/react-dropdown-menu/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw=="], "@radix-ui/react-dropdown-menu/@radix-ui/react-use-controllable-state/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="], + "@radix-ui/react-focus-scope/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="], + "@radix-ui/react-hover-card/@radix-ui/react-dismissable-layer/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ=="], "@radix-ui/react-hover-card/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.0.3", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-use-callback-ref": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg=="], @@ -7199,8 +7177,12 @@ "@radix-ui/react-popper/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg=="], + "@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.0" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw=="], + "@radix-ui/react-progress/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="], + "@radix-ui/react-select/@radix-ui/react-dismissable-layer/@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], + "@radix-ui/react-select/@radix-ui/react-popper/@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], "@radix-ui/react-select/@radix-ui/react-popper/@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.1", "", { "dependencies": { "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w=="], @@ -7237,26 +7219,64 @@ "@smithy/credential-provider-imds/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@3.0.3", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ=="], - "@smithy/signature-v4/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@types/body-parser/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "@smithy/util-stream/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="], + "@types/connect/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], + "@types/express-serve-static-core/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@types/jsdom/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@types/jsonwebtoken/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@types/ldapjs/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@types/node-fetch/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@types/send/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@types/serve-static/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "@types/winston/winston/logform": ["logform@2.6.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ=="], "@types/winston/winston/winston-transport": ["winston-transport@4.7.0", "", { "dependencies": { "logform": "^2.3.2", "readable-stream": "^3.6.0", "triple-beam": "^1.3.0" } }, "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg=="], + "@types/xml-encryption/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "@types/xml2js/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "@vitejs/plugin-react/@babel/core/@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], + + "@vitejs/plugin-react/@babel/core/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], + + "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], + + "@vitejs/plugin-react/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], + + "@vitejs/plugin-react/@babel/core/@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], + + "@vitejs/plugin-react/@babel/core/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], + + "@vitejs/plugin-react/@babel/core/@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], + + "@vitejs/plugin-react/@babel/core/@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], + + "@vitejs/plugin-react/@babel/core/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - "browserify-sign/readable-stream/core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + "body-parser/raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], "browserify-sign/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], "browserify-sign/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "bun-types/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "caniuse-api/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], + "caniuse-api/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], "chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], @@ -7271,14 +7291,34 @@ "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "core-js-compat/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], + + "core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], + + "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], + + "d3-sankey/d3-array/internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="], + + "d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="], + "data-urls/whatwg-url/tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], "data-urls/whatwg-url/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + "eslint/@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], + "expect/jest-message-util/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="], "expect/jest-message-util/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], + "expect/jest-util/@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="], + + "expect/jest-util/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "expect/jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + + "expect/jest-util/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "express-session/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "express-static-gzip/serve-static/send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="], @@ -7287,16 +7327,16 @@ "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "gaxios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + "gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg=="], - "google-auth-library/gaxios/https-proxy-agent": ["https-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.0.2", "debug": "4" } }, "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA=="], + "gaxios/https-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "gcp-metadata/gaxios/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + + "glob/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], "google-auth-library/gcp-metadata/google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="], - "googleapis-common/gaxios/https-proxy-agent": ["https-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.0.2", "debug": "4" } }, "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA=="], - - "gtoken/gaxios/https-proxy-agent": ["https-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.0.2", "debug": "4" } }, "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA=="], - "hast-util-from-html-isomorphic/@types/hast/@types/unist": ["@types/unist@2.0.10", "", {}, "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="], "hast-util-from-html/@types/hast/@types/unist": ["@types/unist@2.0.10", "", {}, "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA=="], @@ -7333,10 +7373,14 @@ "jest-changed-files/execa/strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="], + "jest-circus/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "jest-circus/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], "jest-config/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "jest-config/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "jest-config/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], "jest-config/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -7345,170 +7389,52 @@ "jest-each/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - "jest-environment-node/@jest/fake-timers/@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="], + "jest-environment-jsdom/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "jest-environment-node/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "jest-haste-map/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "jest-leak-detector/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], "jest-message-util/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - "jest-mock/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + "jest-mock/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "jest-runtime/@jest/fake-timers/@sinonjs/fake-timers": ["@sinonjs/fake-timers@13.0.5", "", { "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw=="], + "jest-runner/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "jest-runtime/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "jest-runtime/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - "jest-runtime/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "jest-runtime/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - "jest-snapshot/expect/jest-mock": ["jest-mock@30.2.0", "", { "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", "jest-util": "30.2.0" } }, "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw=="], + "jest-runtime/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], "jest-snapshot/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], "jest-snapshot/synckit/@pkgr/core": ["@pkgr/core@0.2.9", "", {}, "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA=="], - "jest-util/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + "jest-util/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "jest-validate/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + "jest-watcher/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "jest-worker/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "jest-worker/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], "jsdom/whatwg-url/tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], "jsonwebtoken/jws/jwa": ["jwa@1.4.1", "", { "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA=="], + "jszip/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + + "jszip/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + "jwks-rsa/@types/express/@types/express-serve-static-core": ["@types/express-serve-static-core@4.19.6", "", { "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A=="], - "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-firefox-class-in-computed-class-key": ["@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-safari-class-field-initializer-scope": ["@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1", "@babel/traverse": "^7.28.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-remap-async-to-generator": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.28.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/template": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex": ["@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.27.1", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.28.5", "", { "dependencies": { "@babel/helper-module-transforms": "^7.28.3", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.28.4", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-transform-destructuring": "^7.28.0", "@babel/plugin-transform-parameters": "^7.27.7", "@babel/traverse": "^7.28.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.27.7", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.27.1", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-create-class-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.28.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers": ["@babel/plugin-transform-regexp-modifiers@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.27.1", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.14", "", { "dependencies": { "@babel/compat-data": "^7.27.7", "@babel/helper-define-polyfill-provider": "^0.6.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.13.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.6.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg=="], - - "librechat-data-provider/@babel/preset-env/core-js-compat": ["core-js-compat@3.47.0", "", { "dependencies": { "browserslist": "^4.28.0" } }, "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ=="], - - "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-display-name": ["@babel/plugin-transform-react-display-name@7.28.0", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA=="], - - "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-jsx": ["@babel/plugin-transform-react-jsx@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-module-imports": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/types": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw=="], - - "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-jsx-development": ["@babel/plugin-transform-react-jsx-development@7.27.1", "", { "dependencies": { "@babel/plugin-transform-react-jsx": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q=="], - - "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-pure-annotations": ["@babel/plugin-transform-react-pure-annotations@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA=="], - - "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.27.1", "", { "dependencies": { "@babel/helper-module-transforms": "^7.27.1", "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw=="], - - "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript": ["@babel/plugin-transform-typescript@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-create-class-features-plugin": "^7.28.5", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA=="], - "log-update/slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], "log-update/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.0.0", "", { "dependencies": { "get-east-asian-width": "^1.0.0" } }, "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA=="], @@ -7529,17 +7455,35 @@ "pkg-dir/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + "postcss-colormin/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], + "postcss-colormin/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], + "postcss-convert-values/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], + "postcss-convert-values/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], + "postcss-merge-rules/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], + "postcss-merge-rules/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], + "postcss-minify-params/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], + "postcss-minify-params/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], + "postcss-normalize-unicode/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], + "postcss-normalize-unicode/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], - "postcss-preset-env/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], + "postcss-preset-env/autoprefixer/caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="], + + "postcss-preset-env/browserslist/caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="], + + "postcss-preset-env/browserslist/electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="], + + "postcss-preset-env/browserslist/update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], + + "postcss-reduce-initial/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], "postcss-reduce-initial/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], @@ -7573,12 +7517,14 @@ "rollup-plugin-typescript2/@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "stylehacks/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], "stylehacks/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], "sucrase/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "sucrase/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "sucrase/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], "svgo/css-select/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="], @@ -7587,135 +7533,35 @@ "tailwindcss/postcss-load-config/lilconfig": ["lilconfig@3.0.0", "", {}, "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g=="], - "terser-webpack-plugin/jest-worker/supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], - "unist-util-remove-position/unist-util-visit/unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="], "unist-util-remove-position/unist-util-visit/unist-util-visit-parents": ["unist-util-visit-parents@5.1.3", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" } }, "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg=="], + "vasync/verror/core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="], + "vfile-location/vfile/unist-util-stringify-position": ["unist-util-stringify-position@3.0.3", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg=="], "vfile-location/vfile/vfile-message": ["vfile-message@3.1.4", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^3.0.0" } }, "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw=="], - "webpack/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], - - "webpack/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], - - "webpack/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], - "winston-daily-rotate-file/winston-transport/logform": ["logform@2.6.0", "", { "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" } }, "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ=="], - "workbox-build/@babel/preset-env/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": ["@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ=="], + "workbox-build/@babel/core/@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - "workbox-build/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": ["@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-transform-optional-chaining": "^7.23.3" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ=="], + "workbox-build/@babel/core/@babel/generator": ["@babel/generator@7.29.1", "", { "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw=="], - "workbox-build/@babel/preset-env/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": ["@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.7", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw=="], + "workbox-build/@babel/core/@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], - "workbox-build/@babel/preset-env/@babel/plugin-syntax-import-assertions": ["@babel/plugin-syntax-import-assertions@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw=="], + "workbox-build/@babel/core/@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.28.6", "", { "dependencies": { "@babel/helper-module-imports": "^7.28.6", "@babel/helper-validator-identifier": "^7.28.5", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-arrow-functions": ["@babel/plugin-transform-arrow-functions@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ=="], + "workbox-build/@babel/core/@babel/helpers": ["@babel/helpers@7.28.6", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-async-generator-functions": ["@babel/plugin-transform-async-generator-functions@7.23.9", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-remap-async-to-generator": "^7.22.20", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ=="], + "workbox-build/@babel/core/@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-async-to-generator": ["@babel/plugin-transform-async-to-generator@7.23.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-remap-async-to-generator": "^7.22.20" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw=="], + "workbox-build/@babel/core/@babel/template": ["@babel/template@7.28.6", "", { "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-block-scoped-functions": ["@babel/plugin-transform-block-scoped-functions@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A=="], + "workbox-build/@babel/core/@babel/traverse": ["@babel/traverse@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/types": "^7.29.0", "debug": "^4.3.1" } }, "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-block-scoping": ["@babel/plugin-transform-block-scoping@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.23.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block": ["@babel/plugin-transform-class-static-block@7.23.4", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.23.8", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-computed-properties": ["@babel/plugin-transform-computed-properties@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/template": "^7.22.15" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-destructuring": ["@babel/plugin-transform-destructuring@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-dotall-regex": ["@babel/plugin-transform-dotall-regex@7.23.3", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-duplicate-keys": ["@babel/plugin-transform-duplicate-keys@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-dynamic-import": ["@babel/plugin-transform-dynamic-import@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-exponentiation-operator": ["@babel/plugin-transform-exponentiation-operator@7.23.3", "", { "dependencies": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-export-namespace-from": ["@babel/plugin-transform-export-namespace-from@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-for-of": ["@babel/plugin-transform-for-of@7.23.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-function-name": ["@babel/plugin-transform-function-name@7.23.3", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-function-name": "^7.23.0", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-json-strings": ["@babel/plugin-transform-json-strings@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-literals": ["@babel/plugin-transform-literals@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-logical-assignment-operators": ["@babel/plugin-transform-logical-assignment-operators@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-member-expression-literals": ["@babel/plugin-transform-member-expression-literals@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-modules-amd": ["@babel/plugin-transform-modules-amd@7.23.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-modules-commonjs": ["@babel/plugin-transform-modules-commonjs@7.23.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-modules-systemjs": ["@babel/plugin-transform-modules-systemjs@7.23.9", "", { "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-modules-umd": ["@babel/plugin-transform-modules-umd@7.23.3", "", { "dependencies": { "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex": ["@babel/plugin-transform-named-capturing-groups-regex@7.22.5", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-new-target": ["@babel/plugin-transform-new-target@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-numeric-separator": ["@babel/plugin-transform-numeric-separator@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-object-rest-spread": ["@babel/plugin-transform-object-rest-spread@7.23.4", "", { "dependencies": { "@babel/compat-data": "^7.23.3", "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-transform-parameters": "^7.23.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-object-super": ["@babel/plugin-transform-object-super@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-optional-catch-binding": ["@babel/plugin-transform-optional-catch-binding@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.23.4", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-parameters": ["@babel/plugin-transform-parameters@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods": ["@babel/plugin-transform-private-methods@7.23.3", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object": ["@babel/plugin-transform-private-property-in-object@7.23.4", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-property-literals": ["@babel/plugin-transform-property-literals@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-regenerator": ["@babel/plugin-transform-regenerator@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "regenerator-transform": "^0.15.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-reserved-words": ["@babel/plugin-transform-reserved-words@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-shorthand-properties": ["@babel/plugin-transform-shorthand-properties@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-spread": ["@babel/plugin-transform-spread@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-sticky-regex": ["@babel/plugin-transform-sticky-regex@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-template-literals": ["@babel/plugin-transform-template-literals@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-typeof-symbol": ["@babel/plugin-transform-typeof-symbol@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-unicode-escapes": ["@babel/plugin-transform-unicode-escapes@7.23.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-unicode-property-regex": ["@babel/plugin-transform-unicode-property-regex@7.23.3", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-unicode-regex": ["@babel/plugin-transform-unicode-regex@7.23.3", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex": ["@babel/plugin-transform-unicode-sets-regex@7.23.3", "", { "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw=="], - - "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs2": ["babel-plugin-polyfill-corejs2@0.4.8", "", { "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.5.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg=="], - - "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs3": ["babel-plugin-polyfill-corejs3@0.9.0", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.5.0", "core-js-compat": "^3.34.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg=="], - - "workbox-build/@babel/preset-env/babel-plugin-polyfill-regenerator": ["babel-plugin-polyfill-regenerator@0.5.5", "", { "dependencies": { "@babel/helper-define-polyfill-provider": "^0.5.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg=="], - - "workbox-build/@babel/preset-env/core-js-compat": ["core-js-compat@3.35.1", "", { "dependencies": { "browserslist": "^4.22.2" } }, "sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw=="], + "workbox-build/@babel/core/@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="], "workbox-build/@rollup/plugin-replace/@rollup/pluginutils": ["@rollup/pluginutils@3.1.0", "", { "dependencies": { "@types/estree": "0.0.39", "estree-walker": "^1.0.1", "picomatch": "^2.2.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0" } }, "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg=="], @@ -7723,6 +7569,16 @@ "workbox-build/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "workbox-build/glob/foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "workbox-build/glob/jackspeak": ["jackspeak@4.2.3", "", { "dependencies": { "@isaacs/cliui": "^9.0.0" } }, "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg=="], + + "workbox-build/glob/minimatch": ["minimatch@10.1.1", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ=="], + + "workbox-build/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "workbox-build/glob/path-scurry": ["path-scurry@2.0.1", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA=="], + "workbox-build/source-map/whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="], "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], @@ -7735,7 +7591,11 @@ "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], - "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], @@ -7749,28 +7609,26 @@ "@aws-sdk/client-bedrock-agent-runtime/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="], + + "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="], + + "@aws-sdk/client-bedrock-agent-runtime/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@aws-sdk/client-bedrock-agent-runtime/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - - "@aws-sdk/client-bedrock-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.948.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.947.0", "@aws-sdk/middleware-host-header": "3.936.0", "@aws-sdk/middleware-logger": "3.936.0", "@aws-sdk/middleware-recursion-detection": "3.948.0", "@aws-sdk/middleware-user-agent": "3.947.0", "@aws-sdk/region-config-resolver": "3.936.0", "@aws-sdk/types": "3.936.0", "@aws-sdk/util-endpoints": "3.936.0", "@aws-sdk/util-user-agent-browser": "3.936.0", "@aws-sdk/util-user-agent-node": "3.947.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.7", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.14", "@smithy/middleware-retry": "^4.4.14", "@smithy/middleware-serde": "^4.2.6", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.10", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.13", "@smithy/util-defaults-mode-node": "^4.2.16", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iWjchXy8bIAVBUsKnbfKYXRwhLgRg3EqCQ5FTr3JbR+QR75rZm4ZOYXlvHGztVTmtAZ+PQVA1Y4zO7v7N87C0A=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - - "@aws-sdk/client-bedrock-runtime/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - "@aws-sdk/client-cognito-identity/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ=="], "@aws-sdk/client-cognito-identity/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ=="], @@ -7791,7 +7649,11 @@ "@aws-sdk/client-cognito-identity/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ=="], - "@aws-sdk/client-kendra/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@aws-sdk/client-kendra/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@aws-sdk/client-kendra/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@aws-sdk/client-kendra/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], "@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], @@ -7805,10 +7667,18 @@ "@aws-sdk/client-kendra/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/client-kendra/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@aws-sdk/client-kendra/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@aws-sdk/client-kendra/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/client-kendra/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@aws-sdk/client-kendra/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/client-kendra/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@aws-sdk/client-kendra/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@aws-sdk/client-kendra/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], @@ -7873,92 +7743,128 @@ "@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/core": ["@aws-sdk/core@3.758.0", "", { "dependencies": { "@aws-sdk/types": "3.734.0", "@smithy/core": "^3.1.5", "@smithy/node-config-provider": "^4.0.1", "@smithy/property-provider": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/signature-v4": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/util-middleware": "^4.0.1", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.723.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/core": ["@smithy/core@3.1.5", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.2", "@smithy/protocol-http": "^5.0.1", "@smithy/types": "^4.1.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.1", "@smithy/util-stream": "^4.1.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/node-config-provider": ["@smithy/node-config-provider@4.0.1", "", { "dependencies": { "@smithy/property-provider": "^4.0.1", "@smithy/shared-ini-file-loader": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-config-provider": ["@smithy/util-config-provider@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.0", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-serde": "^4.2.7", "@smithy/node-config-provider": "^4.3.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-M6qWfUNny6NFNy8amrCGIb9TfOMUkHVtg9bHtEFGRgfH7A7AtPpn/fcrToGPjVDK1ECuMVvqGQOXcZxmu9K+7A=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-JSbALU3G+JS4kyBZPqnJ3hxIYwOVRV7r9GNQMS6j5VsQDo5+Es5nddLfr9TQlxZLNHPvKSh+XSB0OuWGfSWFcA=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream": ["@smithy/util-stream@4.1.2", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.758.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.758.0", "@aws-sdk/middleware-host-header": "3.734.0", "@aws-sdk/middleware-logger": "3.734.0", "@aws-sdk/middleware-recursion-detection": "3.734.0", "@aws-sdk/middleware-user-agent": "3.758.0", "@aws-sdk/region-config-resolver": "3.734.0", "@aws-sdk/types": "3.734.0", "@aws-sdk/util-endpoints": "3.743.0", "@aws-sdk/util-user-agent-browser": "3.734.0", "@aws-sdk/util-user-agent-node": "3.758.0", "@smithy/config-resolver": "^4.0.1", "@smithy/core": "^3.1.5", "@smithy/fetch-http-handler": "^5.0.1", "@smithy/hash-node": "^4.0.1", "@smithy/invalid-dependency": "^4.0.1", "@smithy/middleware-content-length": "^4.0.1", "@smithy/middleware-endpoint": "^4.0.6", "@smithy/middleware-retry": "^4.0.7", "@smithy/middleware-serde": "^4.0.2", "@smithy/middleware-stack": "^4.0.1", "@smithy/node-config-provider": "^4.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/protocol-http": "^5.0.1", "@smithy/smithy-client": "^4.1.6", "@smithy/types": "^4.1.0", "@smithy/url-parser": "^4.0.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.7", "@smithy/util-defaults-mode-node": "^4.0.7", "@smithy/util-endpoints": "^3.0.1", "@smithy/util-middleware": "^4.0.1", "@smithy/util-retry": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.1.2", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.0.1", "@smithy/node-http-handler": "^4.0.3", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="], - "@aws-sdk/middleware-websocket/@smithy/fetch-http-handler/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="], - "@aws-sdk/middleware-websocket/@smithy/fetch-http-handler/@smithy/util-base64/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw=="], - "@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.2", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ=="], - "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="], - "@aws-sdk/nested-clients/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], - "@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="], - "@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.0.1", "", { "dependencies": { "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA=="], - "@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="], - "@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="], - "@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], - "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], + "@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="], + "@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.0", "", { "dependencies": { "@smithy/core": "^3.19.0", "@smithy/middleware-serde": "^4.2.7", "@smithy/node-config-provider": "^4.3.6", "@smithy/shared-ini-file-loader": "^4.4.1", "@smithy/types": "^4.10.0", "@smithy/url-parser": "^4.2.6", "@smithy/util-middleware": "^4.2.6", "tslib": "^2.6.2" } }, "sha512-M6qWfUNny6NFNy8amrCGIb9TfOMUkHVtg9bHtEFGRgfH7A7AtPpn/fcrToGPjVDK1ECuMVvqGQOXcZxmu9K+7A=="], + "@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-JSbALU3G+JS4kyBZPqnJ3hxIYwOVRV7r9GNQMS6j5VsQDo5+Es5nddLfr9TQlxZLNHPvKSh+XSB0OuWGfSWFcA=="], + "@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream": ["@smithy/util-stream@4.5.7", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.7", "@smithy/node-http-handler": "^4.4.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Uuy4S5Aj4oF6k1z+i2OtIBJUns4mlg29Ph4S+CqjR+f4XXpSFVgTCYLzMszHJTicYDBxKFtwq2/QSEDSS5l02A=="], + "@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], + + "@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], + + "@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], + + "@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], + + "@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], + + "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], + "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], + "@babel/plugin-transform-runtime/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "@babel/plugin-transform-runtime/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], + "@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], + + "@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], + + "@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], + + "@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], + + "@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], + + "@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], + + "@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], + + "@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], + + "@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], + + "@google/genai/google-auth-library/gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + + "@google/genai/google-auth-library/gaxios/rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": "dist/esm/bin.mjs" }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], + + "@grpc/proto-loader/protobufjs/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], "@jest/expect/expect/jest-matcher-utils/pretty-format": ["pretty-format@30.2.0", "", { "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", "react-is": "^18.3.1" } }, "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA=="], - "@jest/fake-timers/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], - "@jest/reporters/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "@jest/reporters/glob/path-scurry/lru-cache": ["lru-cache@10.2.0", "", {}, "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q=="], @@ -7967,6 +7873,8 @@ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/eventstream-handler-node/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="], "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-recursion-detection/@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.1.1", "", {}, "sha512-RcLam17LdlbSOSp9VxmUu1eI6Mwxp+OwhD2QhiSNmNCzoDb0EeUXTD2n/WbcnrAYMGlmf05th6QYq23VqvJqpA=="], @@ -7975,6 +7883,10 @@ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Oy6w7+fzIdr10DhF/HpfVLy6raZFTdiE7pxS1rvpuj2JgxzW2y6urm2sYf3eLOpMiHyuG4xUBwFiJpU9CCEvJA=="], "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/token-providers/@smithy/property-provider": ["@smithy/property-provider@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w=="], @@ -7983,6 +7895,10 @@ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/config-resolver/@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], @@ -8013,6 +7929,8 @@ "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core": ["@aws-sdk/core@3.927.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@aws-sdk/xml-builder": "3.921.0", "@smithy/core": "^3.17.2", "@smithy/node-config-provider": "^4.3.4", "@smithy/property-provider": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/signature-v4": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-QOtR9QdjNeC7bId3fc/6MnqoEezvQ2Fk+x6F+Auf7NhOxwYAtB1nvh0k3+gJHWVGpfxN1I8keahRZd79U68/ag=="], @@ -8023,6 +7941,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], @@ -8051,172 +7971,48 @@ "@langchain/google-gauth/google-auth-library/gaxios/rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": "dist/esm/bin.mjs" }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="], - "@librechat/client/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-for-of/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-spread/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], - - "@librechat/client/@babel/preset-env/core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], - - "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-jsx/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-react/@babel/plugin-transform-react-pure-annotations/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-for-of/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-spread/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], - - "@librechat/frontend/@babel/preset-env/core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], - - "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-jsx/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-react/@babel/plugin-transform-react-pure-annotations/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - "@librechat/frontend/@react-spring/web/@react-spring/shared/@react-spring/rafz": ["@react-spring/rafz@9.7.5", "", {}, "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw=="], "@librechat/frontend/@testing-library/jest-dom/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@librechat/frontend/@testing-library/jest-dom/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "@librechat/frontend/jest-environment-jsdom/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/cssstyle": ["cssstyle@2.3.0", "", { "dependencies": { "cssom": "~0.3.6" } }, "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/data-urls": ["data-urls@3.0.2", "", { "dependencies": { "abab": "^2.0.6", "whatwg-mimetype": "^3.0.0", "whatwg-url": "^11.0.0" } }, "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/html-encoding-sniffer": ["html-encoding-sniffer@3.0.0", "", { "dependencies": { "whatwg-encoding": "^2.0.0" } }, "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/http-proxy-agent": ["http-proxy-agent@5.0.0", "", { "dependencies": { "@tootallnate/once": "2", "agent-base": "6", "debug": "4" } }, "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/nwsapi": ["nwsapi@2.2.7", "", {}, "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/tough-cookie": ["tough-cookie@4.1.3", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/w3c-xmlserializer": ["w3c-xmlserializer@4.0.0", "", { "dependencies": { "xml-name-validator": "^4.0.0" } }, "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/whatwg-encoding": ["whatwg-encoding@2.0.0", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/whatwg-url": ["whatwg-url@11.0.0", "", { "dependencies": { "tr46": "^3.0.0", "webidl-conversions": "^7.0.0" } }, "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/xml-name-validator": ["xml-name-validator@4.0.0", "", {}, "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="], - "@mcp-ui/client/@modelcontextprotocol/sdk/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "@mcp-ui/client/@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], + + "@mcp-ui/client/@modelcontextprotocol/sdk/express/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "@node-saml/passport-saml/@types/express/@types/express-serve-static-core/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + "@node-saml/passport-saml/@types/express/@types/express-serve-static-core/@types/qs": ["@types/qs@6.9.17", "", {}, "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ=="], + "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer/protobufjs/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "@opentelemetry/otlp-transformer/protobufjs/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="], + "@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.0", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA=="], + "@radix-ui/react-progress/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="], "@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.0.1", "", { "dependencies": { "@babel/runtime": "^7.13.10" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw=="], "@radix-ui/react-tabs/@radix-ui/react-roving-focus/@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.0.2", "", { "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-compose-refs": "1.0.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0" } }, "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg=="], + "@vitejs/plugin-react/@babel/core/@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], + + "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/browserslist": ["browserslist@4.28.1", "", { "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" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], + + "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], + + "@vitejs/plugin-react/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], + + "body-parser/raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], "colorspace/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="], @@ -8225,6 +8021,12 @@ "expect/jest-message-util/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + "expect/jest-message-util/@jest/types/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], + + "expect/jest-util/@jest/types/@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + + "expect/jest-util/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "express-static-gzip/serve-static/send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "express-static-gzip/serve-static/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], @@ -8233,11 +8035,11 @@ "express-static-gzip/serve-static/send/mime": ["mime@1.6.0", "", { "bin": "cli.js" }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], - "google-auth-library/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg=="], + "gcp-metadata/gaxios/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], - "googleapis-common/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg=="], + "gcp-metadata/gaxios/https-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], - "gtoken/gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg=="], + "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "jest-changed-files/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], @@ -8245,75 +8047,13 @@ "jest-config/glob/path-scurry/lru-cache": ["lru-cache@10.2.0", "", {}, "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q=="], - "jest-mock/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], - "jest-runtime/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "jest-runtime/glob/path-scurry/lru-cache": ["lru-cache@10.2.0", "", {}, "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q=="], - "jest-util/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], - "jsdom/whatwg-url/tr46/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - "librechat-data-provider/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.27.1", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.1", "@babel/helper-wrap-function": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-for-of/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-spread/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.6.5", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-plugin-utils": "^7.27.1", "debug": "^4.4.1", "lodash.debounce": "^4.0.8", "resolve": "^1.22.10" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg=="], - - "librechat-data-provider/@babel/preset-env/core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], - - "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-jsx/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-react/@babel/plugin-transform-react-pure-annotations/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.5", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ=="], - - "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], + "jwks-rsa/@types/express/@types/express-serve-static-core/@types/node": ["@types/node@20.11.16", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ=="], "mongodb-connection-string-url/whatwg-url/tr46/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], @@ -8331,41 +8071,15 @@ "svgo/css-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], - "terser-webpack-plugin/jest-worker/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + "workbox-build/@babel/core/@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "workbox-build/@babel/preset-env/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="], + "workbox-build/@babel/core/@babel/helper-compilation-targets/@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.22.20", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-wrap-function": "^7.22.20" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw=="], + "workbox-build/@babel/core/@babel/helper-compilation-targets/browserslist": ["browserslist@4.28.1", "", { "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" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator": ["@babel/helper-remap-async-to-generator@7.22.20", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-wrap-function": "^7.22.20" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw=="], + "workbox-build/@babel/core/@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.23.10", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.23.10", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-classes/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-for-of/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-optional-chaining/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.23.10", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.23.10", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-spread/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="], - - "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="], - - "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="], - - "workbox-build/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider": ["@babel/helper-define-polyfill-provider@0.5.0", "", { "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", "debug": "^4.1.1", "lodash.debounce": "^4.0.8", "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q=="], - - "workbox-build/@babel/preset-env/core-js-compat/browserslist": ["browserslist@4.28.0", "", { "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", "electron-to-chromium": "^1.5.249", "node-releases": "^2.0.27", "update-browserslist-db": "^1.1.4" }, "bin": "cli.js" }, "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ=="], + "workbox-build/@babel/core/@babel/helper-module-transforms/@babel/helper-module-imports": ["@babel/helper-module-imports@7.28.6", "", { "dependencies": { "@babel/traverse": "^7.28.6", "@babel/types": "^7.28.6" } }, "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw=="], "workbox-build/@rollup/plugin-replace/@rollup/pluginutils/@types/estree": ["@types/estree@0.0.39", "", {}, "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="], @@ -8373,28 +8087,34 @@ "workbox-build/@rollup/plugin-replace/@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "workbox-build/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="], + + "workbox-build/glob/path-scurry/lru-cache": ["lru-cache@11.2.2", "", {}, "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg=="], + "workbox-build/source-map/whatwg-url/tr46": ["tr46@1.0.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="], "workbox-build/source-map/whatwg-url/webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="], - "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], - "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@aws-sdk/client-bedrock-agent-runtime/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Oy6w7+fzIdr10DhF/HpfVLy6raZFTdiE7pxS1rvpuj2JgxzW2y6urm2sYf3eLOpMiHyuG4xUBwFiJpU9CCEvJA=="], "@aws-sdk/client-bedrock-agent-runtime/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - "@aws-sdk/client-bedrock-agent-runtime/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal/@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], - "@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], + "@aws-sdk/client-bedrock-agent-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal/@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@aws-sdk/client-bedrock-agent-runtime/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@aws-sdk/client-cognito-identity/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@3.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ=="], - "@aws-sdk/client-kendra/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], - "@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Oy6w7+fzIdr10DhF/HpfVLy6raZFTdiE7pxS1rvpuj2JgxzW2y6urm2sYf3eLOpMiHyuG4xUBwFiJpU9CCEvJA=="], "@aws-sdk/client-kendra/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], @@ -8409,76 +8129,86 @@ "@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@3.0.3", "", { "dependencies": { "@smithy/types": "^3.3.0", "tslib": "^2.6.2" } }, "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/core/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.2", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/node-config-provider/@smithy/property-provider": ["@smithy/property-provider@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.6", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tVoyzJ2vXp4R3/aeV4EQjBDmCuWxRa8eo3KybL7Xv4wEM16nObYh7H1sNfcuLWHAAAzb0RVyxUz1S3sGj4X+Tg=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/node-config-provider/@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.0.1", "", { "dependencies": { "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.0.1", "", { "dependencies": { "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.3", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/protocol-http": "^5.0.1", "@smithy/querystring-builder": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], - "@aws-sdk/middleware-websocket/@smithy/fetch-http-handler/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], - "@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], - "@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], - "@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], - "@aws-sdk/token-providers/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-PFMVHVPgtFECeu4iZ+4SX6VOQT0+dIpm4jSPLLL6JLSkp9RohGqKBKD0cbiXdeIFS08Forp0UHI6kc0gIHenSA=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.6", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.6", "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-tVoyzJ2vXp4R3/aeV4EQjBDmCuWxRa8eo3KybL7Xv4wEM16nObYh7H1sNfcuLWHAAAzb0RVyxUz1S3sGj4X+Tg=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.6", "@smithy/querystring-builder": "^4.2.6", "@smithy/types": "^4.10.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-fcVap4QwqmzQwQK9QU3keeEpCzTjnP9NJ171vI7GnD7nbkAIcP9biZhDUx88uRH9BabSsQDS0unUps88uZvFIQ=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/core-js-compat/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.8.28", "", { "bin": "dist/cli.js" }, "sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ=="], "@babel/plugin-transform-runtime/babel-plugin-polyfill-corejs3/core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], + "@google/genai/google-auth-library/gaxios/rimraf/glob": ["glob@10.5.0", "", { "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": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + "@istanbuljs/load-nyc-config/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], "@jest/expect/expect/jest-matcher-utils/pretty-format/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/eventstream-handler-node/@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@aws-sdk/util-format-url/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], @@ -8491,10 +8221,16 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.921.0", "", { "dependencies": { "@smithy/types": "^4.8.1", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q=="], @@ -8503,8 +8239,12 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], @@ -8525,6 +8265,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.921.0", "", { "dependencies": { "@smithy/types": "^4.8.1", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q=="], @@ -8533,10 +8275,16 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA=="], @@ -8579,6 +8327,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], @@ -8595,6 +8345,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], @@ -8605,10 +8357,16 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA=="], @@ -8651,6 +8409,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], @@ -8667,6 +8427,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], @@ -8677,10 +8439,16 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.927.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.927.0", "@aws-sdk/middleware-host-header": "3.922.0", "@aws-sdk/middleware-logger": "3.922.0", "@aws-sdk/middleware-recursion-detection": "3.922.0", "@aws-sdk/middleware-user-agent": "3.927.0", "@aws-sdk/region-config-resolver": "3.925.0", "@aws-sdk/types": "3.922.0", "@aws-sdk/util-endpoints": "3.922.0", "@aws-sdk/util-user-agent-browser": "3.922.0", "@aws-sdk/util-user-agent-node": "3.927.0", "@smithy/config-resolver": "^4.4.2", "@smithy/core": "^3.17.2", "@smithy/fetch-http-handler": "^5.3.5", "@smithy/hash-node": "^4.2.4", "@smithy/invalid-dependency": "^4.2.4", "@smithy/middleware-content-length": "^4.2.4", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-retry": "^4.4.6", "@smithy/middleware-serde": "^4.2.4", "@smithy/middleware-stack": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/node-http-handler": "^4.4.4", "@smithy/protocol-http": "^5.3.4", "@smithy/smithy-client": "^4.9.2", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.5", "@smithy/util-defaults-mode-node": "^4.2.8", "@smithy/util-endpoints": "^3.2.4", "@smithy/util-middleware": "^4.2.4", "@smithy/util-retry": "^4.2.4", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Oy6w7+fzIdr10DhF/HpfVLy6raZFTdiE7pxS1rvpuj2JgxzW2y6urm2sYf3eLOpMiHyuG4xUBwFiJpU9CCEvJA=="], @@ -8691,10 +8459,16 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.3.4", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.4", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.922.0", "", { "dependencies": { "@aws-sdk/types": "3.922.0", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA=="], @@ -8737,6 +8511,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], @@ -8753,6 +8529,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], @@ -8761,387 +8539,41 @@ "@langchain/google-gauth/google-auth-library/gaxios/rimraf/glob": ["glob@10.5.0", "", { "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": "dist/esm/bin.mjs" }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "@librechat/client/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - - "@librechat/client/@babel/preset-env/core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], - - "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/client/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "@librechat/frontend/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - - "@librechat/frontend/@babel/preset-env/core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], - - "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "@librechat/frontend/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - "@librechat/frontend/@testing-library/jest-dom/chalk/supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - "@librechat/frontend/jest-environment-jsdom/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "@node-saml/passport-saml/@types/express/@types/express-serve-static-core/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "@librechat/frontend/jest-environment-jsdom/jsdom/cssstyle/cssom": ["cssom@0.3.8", "", {}, "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="], + "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer/protobufjs/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "@librechat/frontend/jest-environment-jsdom/jsdom/http-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/browserslist/caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="], - "@librechat/frontend/jest-environment-jsdom/jsdom/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/browserslist/electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="], - "@librechat/frontend/jest-environment-jsdom/jsdom/tough-cookie/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/browserslist/update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], - "@librechat/frontend/jest-environment-jsdom/jsdom/tough-cookie/universalify": ["universalify@0.2.0", "", {}, "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/whatwg-url/tr46": ["tr46@3.0.0", "", { "dependencies": { "punycode": "^2.1.1" } }, "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA=="], + "@vitejs/plugin-react/@babel/core/@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "expect/jest-message-util/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "expect/jest-message-util/@jest/types/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "expect/jest-util/@jest/types/@jest/schemas/@sinclair/typebox": ["@sinclair/typebox@0.27.8", "", {}, "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA=="], + "express-static-gzip/serve-static/send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.28.3", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.2" } }, "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/@babel/helper-annotate-as-pure": ["@babel/helper-annotate-as-pure@7.27.3", "", { "dependencies": { "@babel/types": "^7.27.3" } }, "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core": ["regexpu-core@6.4.0", "", { "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" } }, "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "librechat-data-provider/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], - - "librechat-data-provider/@babel/preset-env/core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], - - "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.28.5", "", { "dependencies": { "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5" } }, "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg=="], - - "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" } }, "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw=="], - - "librechat-data-provider/@babel/preset-typescript/@babel/plugin-transform-typescript/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.27.1", "", { "dependencies": { "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/traverse": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA=="], + "jwks-rsa/@types/express/@types/express-serve-static-core/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], "svgo/css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-async-generator-functions/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.22.20", "", { "dependencies": { "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.15", "@babel/types": "^7.22.19" } }, "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw=="], + "workbox-build/@babel/core/@babel/helper-compilation-targets/browserslist/caniuse-lite": ["caniuse-lite@1.0.30001777", "", {}, "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-async-to-generator/@babel/helper-remap-async-to-generator/@babel/helper-wrap-function": ["@babel/helper-wrap-function@7.22.20", "", { "dependencies": { "@babel/helper-function-name": "^7.22.5", "@babel/template": "^7.22.15", "@babel/types": "^7.22.19" } }, "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw=="], + "workbox-build/@babel/core/@babel/helper-compilation-targets/browserslist/electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="], + "workbox-build/@babel/core/@babel/helper-compilation-targets/browserslist/update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-properties/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-class-static-block/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-classes/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-object-super/@babel/helper-replace-supers/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-methods/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-member-expression-to-functions": ["@babel/helper-member-expression-to-functions@7.23.0", "", { "dependencies": { "@babel/types": "^7.23.0" } }, "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-optimise-call-expression": ["@babel/helper-optimise-call-expression@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-replace-supers": ["@babel/helper-replace-supers@7.22.20", "", { "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw=="], - - "workbox-build/@babel/preset-env/@babel/plugin-transform-private-property-in-object/@babel/helper-create-class-features-plugin/@babel/helper-skip-transparent-expression-wrappers": ["@babel/helper-skip-transparent-expression-wrappers@7.22.5", "", { "dependencies": { "@babel/types": "^7.22.5" } }, "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q=="], - - "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs2/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], - - "workbox-build/@babel/preset-env/babel-plugin-polyfill-corejs3/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], - - "workbox-build/@babel/preset-env/babel-plugin-polyfill-regenerator/@babel/helper-define-polyfill-provider/resolve": ["resolve@1.22.8", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="], - - "workbox-build/@babel/preset-env/core-js-compat/browserslist/update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": "cli.js" }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="], + "workbox-build/@babel/core/@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "workbox-build/source-map/whatwg-url/tr46/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], @@ -9149,25 +8581,43 @@ "@aws-sdk/client-kendra/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-YmWxl32SQRw/kIRccSOxzS/Ib8/b5/f9ex0r5PR40jRJg8X1wgM3KrR2In+8zvOGVhRSXgvyQpw9yOSlmfmSnA=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="], - "@aws-sdk/credential-provider-login/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], - "@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.6", "", { "dependencies": { "@smithy/types": "^4.10.0", "tslib": "^2.6.2" } }, "sha512-YmWxl32SQRw/kIRccSOxzS/Ib8/b5/f9ex0r5PR40jRJg8X1wgM3KrR2In+8zvOGVhRSXgvyQpw9yOSlmfmSnA=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="], - "@aws-sdk/token-providers/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="], - "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], + + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], + + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@aws-sdk/s3-request-presigner/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@google/genai/google-auth-library/gaxios/rimraf/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "@google/genai/google-auth-library/gaxios/rimraf/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "@google/genai/google-auth-library/gaxios/rimraf/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@aws-sdk/middleware-websocket/@aws-sdk/util-format-url/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-browser/@smithy/eventstream-serde-universal/@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/client-bedrock-runtime/@smithy/eventstream-serde-node/@smithy/eventstream-serde-universal/@smithy/eventstream-codec/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], @@ -9175,6 +8625,14 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="], @@ -9185,37 +8643,51 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/fetch-http-handler/@smithy/util-base64/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/fetch-http-handler/@smithy/util-base64/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.4", "", { "dependencies": { "@smithy/property-provider": "^4.2.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], @@ -9223,6 +8695,14 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="], @@ -9239,12 +8719,16 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], @@ -9259,14 +8743,20 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="], @@ -9283,12 +8773,16 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/middleware-retry/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], @@ -9303,14 +8797,20 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="], @@ -9361,6 +8861,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/protocol-http": ["@smithy/protocol-http@5.3.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/smithy-client": ["@smithy/smithy-client@4.9.2", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-endpoint": "^4.3.6", "@smithy/middleware-stack": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "@smithy/util-stream": "^4.5.5", "tslib": "^2.6.2" } }, "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], @@ -9377,18 +8879,26 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-middleware": ["@smithy/util-middleware@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-retry": ["@smithy/util-retry@4.2.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/signature-v4/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/signature-v4/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/signature-v4/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.3.6", "", { "dependencies": { "@smithy/core": "^3.17.2", "@smithy/middleware-serde": "^4.2.4", "@smithy/node-config-provider": "^4.3.4", "@smithy/shared-ini-file-loader": "^4.3.4", "@smithy/types": "^4.8.1", "@smithy/url-parser": "^4.2.4", "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" } }, "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA=="], @@ -9405,12 +8915,16 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], @@ -9427,137 +8941,21 @@ "@langchain/google-gauth/google-auth-library/gaxios/rimraf/glob/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + "@langchain/google-gauth/google-auth-library/gaxios/rimraf/glob/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + "@langchain/google-gauth/google-auth-library/gaxios/rimraf/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], + "@aws-sdk/s3-request-presigner/@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], + "@aws-sdk/s3-request-presigner/@smithy/middleware-endpoint/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], + "@google/genai/google-auth-library/gaxios/rimraf/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - "@librechat/client/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/client/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "@librechat/frontend/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@librechat/frontend/jest-environment-jsdom/jsdom/whatwg-url/tr46/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-dotall-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-duplicate-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-named-capturing-groups-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-regexp-modifiers/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-property-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/regjsparser": ["regjsparser@0.13.0", "", { "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q=="], - - "librechat-data-provider/@babel/preset-env/@babel/plugin-transform-unicode-sets-regex/@babel/helper-create-regexp-features-plugin/regexpu-core/unicode-match-property-value-ecmascript": ["unicode-match-property-value-ecmascript@2.2.1", "", {}, "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg=="], - - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], + "@google/genai/google-auth-library/gaxios/rimraf/glob/path-scurry/lru-cache": ["lru-cache@10.2.0", "", {}, "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="], @@ -9565,6 +8963,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], @@ -9575,12 +8975,12 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], @@ -9593,14 +8993,14 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser/@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-aHb5cqXZocdzEkZ/CvhVjdw5l4r1aU/9iMEyoKzH4eXMowT6M0YjBpp7W/+XjkBnY8Xh0kVd55GKjnPKlCwinQ=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], @@ -9611,28 +9011,38 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], @@ -9643,28 +9053,38 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], @@ -9675,6 +9095,8 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], @@ -9685,12 +9107,16 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream": ["@smithy/util-stream@4.5.5", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.5", "@smithy/node-http-handler": "^4.4.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/core/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1" } }, "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/middleware-retry/@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.4", "", { "dependencies": { "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig=="], @@ -9705,14 +9131,14 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.1.1", "", {}, "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw=="], - "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.5", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.4", "", { "dependencies": { "@smithy/abort-controller": "^4.2.4", "@smithy/protocol-http": "^5.3.4", "@smithy/querystring-builder": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.4", "", { "dependencies": { "@smithy/protocol-http": "^5.3.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/middleware-endpoint/@smithy/url-parser": ["@smithy/url-parser@4.2.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.4", "@smithy/types": "^4.8.1", "tslib": "^2.6.2" } }, "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg=="], @@ -9723,16 +9149,26 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], @@ -9827,10 +9263,18 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/hash-node/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-base64/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], @@ -9857,8 +9301,48 @@ "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/core/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/smithy-client/@smithy/util-stream/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], + + "@langchain/aws/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/core/@smithy/smithy-client/@smithy/util-stream/@smithy/node-http-handler/@smithy/querystring-builder/@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], } } diff --git a/client/jest.config.cjs b/client/jest.config.cjs index 53d4063a0a..f97adb39ce 100644 --- a/client/jest.config.cjs +++ b/client/jest.config.cjs @@ -1,4 +1,4 @@ -/** v0.8.2 */ +/** v0.8.4-rc1 */ module.exports = { roots: ['/src'], testEnvironment: 'jsdom', @@ -32,6 +32,7 @@ module.exports = { '^librechat-data-provider/react-query$': '/../node_modules/librechat-data-provider/src/react-query', }, + maxWorkers: '50%', restoreMocks: true, testResultsProcessor: 'jest-junit', coverageReporters: ['text', 'cobertura', 'lcov'], diff --git a/client/package.json b/client/package.json index cd40c7c7c6..a3ff5529e5 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@librechat/frontend", - "version": "v0.8.2", + "version": "v0.8.4-rc1", "description": "", "type": "module", "scripts": { @@ -38,6 +38,7 @@ "@librechat/client": "*", "@marsidev/react-turnstile": "^1.1.0", "@mcp-ui/client": "^5.7.0", + "@monaco-editor/react": "^4.7.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-alert-dialog": "1.0.2", "@radix-ui/react-checkbox": "^1.0.3", @@ -80,7 +81,7 @@ "lodash": "^4.17.23", "lucide-react": "^0.394.0", "match-sorter": "^8.1.0", - "mermaid": "^11.12.2", + "mermaid": "^11.13.0", "micromark-extension-llm-math": "^3.1.0", "qrcode.react": "^4.2.0", "rc-input-number": "^7.4.2", @@ -93,7 +94,6 @@ "react-gtm-module": "^2.0.11", "react-hook-form": "^7.43.9", "react-i18next": "^15.4.0", - "react-lazy-load-image-component": "^1.6.0", "react-markdown": "^9.0.1", "react-resizable-panels": "^3.0.6", "react-router-dom": "^6.30.3", @@ -122,6 +122,7 @@ "@babel/preset-env": "^7.22.15", "@babel/preset-react": "^7.22.15", "@babel/preset-typescript": "^7.22.15", + "@happy-dom/jest-environment": "^20.8.3", "@tanstack/react-query-devtools": "^4.29.0", "@testing-library/dom": "^9.3.0", "@testing-library/jest-dom": "^5.16.5", @@ -130,10 +131,10 @@ "@types/jest": "^29.5.14", "@types/js-cookie": "^3.0.6", "@types/lodash": "^4.17.15", - "@types/node": "^20.3.0", + "@types/node": "^20.19.35", "@types/react": "^18.2.11", "@types/react-dom": "^18.2.4", - "@vitejs/plugin-react": "^4.3.4", + "@vitejs/plugin-react": "^5.1.4", "autoprefixer": "^10.4.20", "babel-plugin-replace-ts-export-assignment": "^0.0.2", "babel-plugin-root-import": "^6.6.0", @@ -144,16 +145,17 @@ "identity-obj-proxy": "^3.0.0", "jest": "^30.2.0", "jest-canvas-mock": "^2.5.2", - "jest-environment-jsdom": "^29.7.0", + "jest-environment-jsdom": "^30.2.0", "jest-file-loader": "^1.0.3", "jest-junit": "^16.0.0", + "monaco-editor": "^0.55.1", "postcss": "^8.4.31", - "postcss-preset-env": "^8.2.0", + "postcss-preset-env": "^11.2.0", "tailwindcss": "^3.4.1", "typescript": "^5.3.3", - "vite": "^6.4.1", + "vite": "^7.3.1", "vite-plugin-compression2": "^2.2.1", - "vite-plugin-node-polyfills": "^0.23.0", - "vite-plugin-pwa": "^0.21.2" + "vite-plugin-node-polyfills": "^0.25.0", + "vite-plugin-pwa": "^1.2.0" } } diff --git a/client/public/assets/azure-ai-search.svg b/client/public/assets/azure-ai-search.svg new file mode 100644 index 0000000000..5db3422b9b --- /dev/null +++ b/client/public/assets/azure-ai-search.svg @@ -0,0 +1 @@ + diff --git a/client/public/assets/bfl-ai.svg b/client/public/assets/bfl-ai.svg new file mode 100644 index 0000000000..c8556b8557 --- /dev/null +++ b/client/public/assets/bfl-ai.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/client/public/assets/calculator.svg b/client/public/assets/calculator.svg new file mode 100644 index 0000000000..440367fe9e --- /dev/null +++ b/client/public/assets/calculator.svg @@ -0,0 +1 @@ + diff --git a/client/public/assets/google-search.svg b/client/public/assets/google-search.svg new file mode 100644 index 0000000000..be3c8db3d5 --- /dev/null +++ b/client/public/assets/google-search.svg @@ -0,0 +1 @@ + diff --git a/client/public/assets/stability-ai.svg b/client/public/assets/stability-ai.svg new file mode 100644 index 0000000000..bdc74a14d6 --- /dev/null +++ b/client/public/assets/stability-ai.svg @@ -0,0 +1 @@ + diff --git a/client/public/assets/tavily.svg b/client/public/assets/tavily.svg new file mode 100644 index 0000000000..544d55319b --- /dev/null +++ b/client/public/assets/tavily.svg @@ -0,0 +1,3 @@ + + + diff --git a/client/src/Providers/ArtifactsContext.tsx b/client/src/Providers/ArtifactsContext.tsx index 139f679003..fd67d5af94 100644 --- a/client/src/Providers/ArtifactsContext.tsx +++ b/client/src/Providers/ArtifactsContext.tsx @@ -1,7 +1,8 @@ import React, { createContext, useContext, useMemo } from 'react'; +import { useRecoilValue } from 'recoil'; import type { TMessage } from 'librechat-data-provider'; -import { useChatContext } from './ChatContext'; import { getLatestText } from '~/utils'; +import store from '~/store'; export interface ArtifactsContextValue { isSubmitting: boolean; @@ -18,27 +19,28 @@ interface ArtifactsProviderProps { } export function ArtifactsProvider({ children, value }: ArtifactsProviderProps) { - const { isSubmitting, latestMessage, conversation } = useChatContext(); + const isSubmitting = useRecoilValue(store.isSubmittingFamily(0)); + const latestMessage = useRecoilValue(store.latestMessageFamily(0)); + const conversationId = useRecoilValue(store.conversationIdByIndex(0)); const chatLatestMessageText = useMemo(() => { return getLatestText({ - messageId: latestMessage?.messageId ?? null, text: latestMessage?.text ?? null, content: latestMessage?.content ?? null, + messageId: latestMessage?.messageId ?? null, } as TMessage); }, [latestMessage?.messageId, latestMessage?.text, latestMessage?.content]); const defaultContextValue = useMemo( () => ({ isSubmitting, + conversationId: conversationId ?? null, latestMessageText: chatLatestMessageText, latestMessageId: latestMessage?.messageId ?? null, - conversationId: conversation?.conversationId ?? null, }), - [isSubmitting, chatLatestMessageText, latestMessage?.messageId, conversation?.conversationId], + [isSubmitting, chatLatestMessageText, latestMessage?.messageId, conversationId], ); - /** Context value only created when relevant values change */ const contextValue = useMemo( () => (value ? { ...defaultContextValue, ...value } : defaultContextValue), [defaultContextValue, value], diff --git a/client/src/Providers/BadgeRowContext.tsx b/client/src/Providers/BadgeRowContext.tsx index 40df795aba..dce1c38a78 100644 --- a/client/src/Providers/BadgeRowContext.tsx +++ b/client/src/Providers/BadgeRowContext.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useContext, useEffect, useRef } from 'react'; +import React, { createContext, useContext, useEffect, useMemo, useRef } from 'react'; import { useSetRecoilState } from 'recoil'; import { Tools, Constants, LocalStorageKeys, AgentCapabilities } from 'librechat-data-provider'; import type { TAgentsEndpoint } from 'librechat-data-provider'; @@ -9,11 +9,13 @@ import { useCodeApiKeyForm, useToolToggle, } from '~/hooks'; -import { getTimestampedValue, setTimestamp } from '~/utils/timestamps'; +import { getTimestampedValue } from '~/utils/timestamps'; +import { useGetStartupConfig } from '~/data-provider'; import { ephemeralAgentByConvoId } from '~/store'; interface BadgeRowContextType { conversationId?: string | null; + storageContextKey?: string; agentsConfig?: TAgentsEndpoint | null; webSearch: ReturnType; artifacts: ReturnType; @@ -38,34 +40,70 @@ interface BadgeRowProviderProps { children: React.ReactNode; isSubmitting?: boolean; conversationId?: string | null; + specName?: string | null; } export default function BadgeRowProvider({ children, isSubmitting, conversationId, + specName, }: BadgeRowProviderProps) { - const lastKeyRef = useRef(''); + const lastContextKeyRef = useRef(''); const hasInitializedRef = useRef(false); const { agentsConfig } = useGetAgentsConfig(); + const { data: startupConfig } = useGetStartupConfig(); const key = conversationId ?? Constants.NEW_CONVO; + const hasModelSpecs = (startupConfig?.modelSpecs?.list?.length ?? 0) > 0; + + /** + * Compute the storage context key for non-spec persistence: + * - `__defaults__`: specs configured but none active → shared defaults key + * - undefined: spec active (no persistence) or no specs configured (original behavior) + * + * When a spec is active, tool/MCP state is NOT persisted — the admin's spec + * configuration is always applied fresh. Only non-spec user preferences persist. + */ + const storageContextKey = useMemo(() => { + if (!specName && hasModelSpecs) { + return Constants.spec_defaults_key as string; + } + return undefined; + }, [specName, hasModelSpecs]); + + /** + * Compute the storage suffix for reading localStorage defaults: + * - New conversations read from environment key (spec or non-spec defaults) + * - Existing conversations read from conversation key (per-conversation state) + */ + const isNewConvo = key === Constants.NEW_CONVO; + const storageSuffix = isNewConvo && storageContextKey ? storageContextKey : key; const setEphemeralAgent = useSetRecoilState(ephemeralAgentByConvoId(key)); - /** Initialize ephemeralAgent from localStorage on mount and when conversation changes */ + /** Initialize ephemeralAgent from localStorage on mount and when conversation/spec changes. + * Skipped when a spec is active — applyModelSpecEphemeralAgent handles both new conversations + * (pure spec values) and existing conversations (spec values + localStorage overrides). */ useEffect(() => { if (isSubmitting) { return; } - // Check if this is a new conversation or the first load - if (!hasInitializedRef.current || lastKeyRef.current !== key) { + if (specName) { + // Spec active: applyModelSpecEphemeralAgent handles all state (spec base + localStorage + // overrides for existing conversations). Reset init flag so switching back to non-spec + // triggers a fresh re-init. + hasInitializedRef.current = false; + return; + } + // Check if this is a new conversation/spec or the first load + if (!hasInitializedRef.current || lastContextKeyRef.current !== storageSuffix) { hasInitializedRef.current = true; - lastKeyRef.current = key; + lastContextKeyRef.current = storageSuffix; - const codeToggleKey = `${LocalStorageKeys.LAST_CODE_TOGGLE_}${key}`; - const webSearchToggleKey = `${LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_}${key}`; - const fileSearchToggleKey = `${LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_}${key}`; - const artifactsToggleKey = `${LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_}${key}`; + const codeToggleKey = `${LocalStorageKeys.LAST_CODE_TOGGLE_}${storageSuffix}`; + const webSearchToggleKey = `${LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_}${storageSuffix}`; + const fileSearchToggleKey = `${LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_}${storageSuffix}`; + const artifactsToggleKey = `${LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_}${storageSuffix}`; const codeToggleValue = getTimestampedValue(codeToggleKey); const webSearchToggleValue = getTimestampedValue(webSearchToggleKey); @@ -106,39 +144,53 @@ export default function BadgeRowProvider({ } } - /** - * Always set values for all tools (use defaults if not in `localStorage`) - * If `ephemeralAgent` is `null`, create a new object with just our tool values - */ - const finalValues = { - [Tools.execute_code]: initialValues[Tools.execute_code] ?? false, - [Tools.web_search]: initialValues[Tools.web_search] ?? false, - [Tools.file_search]: initialValues[Tools.file_search] ?? false, - [AgentCapabilities.artifacts]: initialValues[AgentCapabilities.artifacts] ?? false, - }; + const hasOverrides = Object.keys(initialValues).length > 0; - setEphemeralAgent((prev) => ({ - ...(prev || {}), - ...finalValues, - })); - - Object.entries(finalValues).forEach(([toolKey, value]) => { - if (value !== false) { - let storageKey = artifactsToggleKey; - if (toolKey === Tools.execute_code) { - storageKey = codeToggleKey; - } else if (toolKey === Tools.web_search) { - storageKey = webSearchToggleKey; - } else if (toolKey === Tools.file_search) { - storageKey = fileSearchToggleKey; + /** Read persisted MCP values from localStorage */ + let mcpOverrides: string[] | null = null; + const mcpStorageKey = `${LocalStorageKeys.LAST_MCP_}${storageSuffix}`; + const mcpRaw = localStorage.getItem(mcpStorageKey); + if (mcpRaw !== null) { + try { + const parsed = JSON.parse(mcpRaw); + if (Array.isArray(parsed) && parsed.length > 0) { + mcpOverrides = parsed; } - // Store the value and set timestamp for existing values - localStorage.setItem(storageKey, JSON.stringify(value)); - setTimestamp(storageKey); + } catch (e) { + console.error('Failed to parse MCP values:', e); } + } + + setEphemeralAgent((prev) => { + if (prev == null) { + /** ephemeralAgent is null — use localStorage defaults */ + if (hasOverrides || mcpOverrides) { + const result = { ...initialValues }; + if (mcpOverrides) { + result.mcp = mcpOverrides; + } + return result; + } + return prev; + } + /** ephemeralAgent already has values (from prior state). + * Only fill in undefined keys from localStorage. */ + let changed = false; + const result = { ...prev }; + for (const [toolKey, value] of Object.entries(initialValues)) { + if (result[toolKey] === undefined) { + result[toolKey] = value; + changed = true; + } + } + if (mcpOverrides && result.mcp === undefined) { + result.mcp = mcpOverrides; + changed = true; + } + return changed ? result : prev; }); } - }, [key, isSubmitting, setEphemeralAgent]); + }, [storageSuffix, specName, isSubmitting, setEphemeralAgent]); /** CodeInterpreter hooks */ const codeApiKeyForm = useCodeApiKeyForm({}); @@ -146,6 +198,7 @@ export default function BadgeRowProvider({ const codeInterpreter = useToolToggle({ conversationId, + storageContextKey, setIsDialogOpen: setCodeDialogOpen, toolKey: Tools.execute_code, localStorageKey: LocalStorageKeys.LAST_CODE_TOGGLE_, @@ -161,6 +214,7 @@ export default function BadgeRowProvider({ const webSearch = useToolToggle({ conversationId, + storageContextKey, toolKey: Tools.web_search, localStorageKey: LocalStorageKeys.LAST_WEB_SEARCH_TOGGLE_, setIsDialogOpen: setWebSearchDialogOpen, @@ -173,6 +227,7 @@ export default function BadgeRowProvider({ /** FileSearch hook */ const fileSearch = useToolToggle({ conversationId, + storageContextKey, toolKey: Tools.file_search, localStorageKey: LocalStorageKeys.LAST_FILE_SEARCH_TOGGLE_, isAuthenticated: true, @@ -181,12 +236,13 @@ export default function BadgeRowProvider({ /** Artifacts hook - using a custom key since it's not a Tool but a capability */ const artifacts = useToolToggle({ conversationId, + storageContextKey, toolKey: AgentCapabilities.artifacts, localStorageKey: LocalStorageKeys.LAST_ARTIFACTS_TOGGLE_, isAuthenticated: true, }); - const mcpServerManager = useMCPServerManager({ conversationId }); + const mcpServerManager = useMCPServerManager({ conversationId, storageContextKey }); const value: BadgeRowContextType = { webSearch, @@ -194,6 +250,7 @@ export default function BadgeRowProvider({ fileSearch, agentsConfig, conversationId, + storageContextKey, codeApiKeyForm, codeInterpreter, searchApiKeyForm, diff --git a/client/src/Providers/DragDropContext.tsx b/client/src/Providers/DragDropContext.tsx index e5a2177f2d..b519c0171f 100644 --- a/client/src/Providers/DragDropContext.tsx +++ b/client/src/Providers/DragDropContext.tsx @@ -1,5 +1,5 @@ import React, { createContext, useContext, useMemo } from 'react'; -import { getEndpointField, isAgentsEndpoint } from 'librechat-data-provider'; +import { isAgentsEndpoint, resolveEndpointType } from 'librechat-data-provider'; import type { EModelEndpoint } from 'librechat-data-provider'; import { useGetEndpointsQuery, useGetAgentByIdQuery } from '~/data-provider'; import { useAgentsMapContext } from './AgentsMapContext'; @@ -9,7 +9,7 @@ interface DragDropContextValue { conversationId: string | null | undefined; agentId: string | null | undefined; endpoint: string | null | undefined; - endpointType?: EModelEndpoint | undefined; + endpointType?: EModelEndpoint | string | undefined; useResponsesApi?: boolean; } @@ -20,13 +20,6 @@ export function DragDropProvider({ children }: { children: React.ReactNode }) { const { data: endpointsConfig } = useGetEndpointsQuery(); const agentsMap = useAgentsMapContext(); - const endpointType = useMemo(() => { - return ( - getEndpointField(endpointsConfig, conversation?.endpoint, 'type') || - (conversation?.endpoint as EModelEndpoint | undefined) - ); - }, [conversation?.endpoint, endpointsConfig]); - const needsAgentFetch = useMemo(() => { const isAgents = isAgentsEndpoint(conversation?.endpoint); if (!isAgents || !conversation?.agent_id) { @@ -40,6 +33,20 @@ export function DragDropProvider({ children }: { children: React.ReactNode }) { enabled: needsAgentFetch, }); + const agentProvider = useMemo(() => { + const isAgents = isAgentsEndpoint(conversation?.endpoint); + if (!isAgents || !conversation?.agent_id) { + return undefined; + } + const agent = agentData || agentsMap?.[conversation.agent_id]; + return agent?.provider; + }, [conversation?.endpoint, conversation?.agent_id, agentData, agentsMap]); + + const endpointType = useMemo( + () => resolveEndpointType(endpointsConfig, conversation?.endpoint, agentProvider), + [endpointsConfig, conversation?.endpoint, agentProvider], + ); + const useResponsesApi = useMemo(() => { const isAgents = isAgentsEndpoint(conversation?.endpoint); if (!isAgents || !conversation?.agent_id || conversation?.useResponsesApi) { diff --git a/client/src/Providers/MessagesViewContext.tsx b/client/src/Providers/MessagesViewContext.tsx index f8f5eef12a..f1cae204a4 100644 --- a/client/src/Providers/MessagesViewContext.tsx +++ b/client/src/Providers/MessagesViewContext.tsx @@ -18,7 +18,8 @@ interface MessagesViewContextValue { /** Message state management */ index: ReturnType['index']; - latestMessage: ReturnType['latestMessage']; + latestMessageId: ReturnType['latestMessageId']; + latestMessageDepth: ReturnType['latestMessageDepth']; setLatestMessage: ReturnType['setLatestMessage']; getMessages: ReturnType['getMessages']; setMessages: ReturnType['setMessages']; @@ -39,7 +40,8 @@ export function MessagesViewProvider({ children }: { children: React.ReactNode } regenerate, isSubmitting, conversation, - latestMessage, + latestMessageId, + latestMessageDepth, setAbortScroll, handleContinue, setLatestMessage, @@ -83,10 +85,11 @@ export function MessagesViewProvider({ children }: { children: React.ReactNode } const messageState = useMemo( () => ({ index, - latestMessage, + latestMessageId, + latestMessageDepth, setLatestMessage, }), - [index, latestMessage, setLatestMessage], + [index, latestMessageId, latestMessageDepth, setLatestMessage], ); /** Combine all values into final context value */ @@ -139,9 +142,9 @@ export function useMessagesOperations() { /** Hook for components that only need message state */ export function useMessagesState() { - const { index, latestMessage, setLatestMessage } = useMessagesViewContext(); + const { index, latestMessageId, latestMessageDepth, setLatestMessage } = useMessagesViewContext(); return useMemo( - () => ({ index, latestMessage, setLatestMessage }), - [index, latestMessage, setLatestMessage], + () => ({ index, latestMessageId, latestMessageDepth, setLatestMessage }), + [index, latestMessageId, latestMessageDepth, setLatestMessage], ); } diff --git a/client/src/Providers/__tests__/DragDropContext.spec.tsx b/client/src/Providers/__tests__/DragDropContext.spec.tsx new file mode 100644 index 0000000000..3c5e0f0796 --- /dev/null +++ b/client/src/Providers/__tests__/DragDropContext.spec.tsx @@ -0,0 +1,134 @@ +import React from 'react'; +import { renderHook } from '@testing-library/react'; +import { EModelEndpoint } from 'librechat-data-provider'; +import type { TEndpointsConfig, Agent } from 'librechat-data-provider'; +import { DragDropProvider, useDragDropContext } from '../DragDropContext'; + +const mockEndpointsConfig: TEndpointsConfig = { + [EModelEndpoint.openAI]: { userProvide: false, order: 0 }, + [EModelEndpoint.agents]: { userProvide: false, order: 1 }, + [EModelEndpoint.anthropic]: { userProvide: false, order: 6 }, + Moonshot: { type: EModelEndpoint.custom, userProvide: false, order: 9999 }, + 'Some Endpoint': { type: EModelEndpoint.custom, userProvide: false, order: 9999 }, +}; + +let mockConversation: Record | null = null; +let mockAgentsMap: Record> = {}; +let mockAgentQueryData: Partial | undefined; + +jest.mock('~/data-provider', () => ({ + useGetEndpointsQuery: () => ({ data: mockEndpointsConfig }), + useGetAgentByIdQuery: () => ({ data: mockAgentQueryData }), +})); + +jest.mock('../AgentsMapContext', () => ({ + useAgentsMapContext: () => mockAgentsMap, +})); + +jest.mock('../ChatContext', () => ({ + useChatContext: () => ({ conversation: mockConversation }), +})); + +function wrapper({ children }: { children: React.ReactNode }) { + return {children}; +} + +describe('DragDropContext endpointType resolution', () => { + beforeEach(() => { + mockConversation = null; + mockAgentsMap = {}; + mockAgentQueryData = undefined; + }); + + describe('non-agents endpoints', () => { + it('resolves custom endpoint type for a custom endpoint', () => { + mockConversation = { endpoint: 'Moonshot' }; + const { result } = renderHook(() => useDragDropContext(), { wrapper }); + expect(result.current.endpointType).toBe(EModelEndpoint.custom); + }); + + it('resolves endpoint name for a standard endpoint', () => { + mockConversation = { endpoint: EModelEndpoint.openAI }; + const { result } = renderHook(() => useDragDropContext(), { wrapper }); + expect(result.current.endpointType).toBe(EModelEndpoint.openAI); + }); + }); + + describe('agents endpoint with provider from agentsMap', () => { + it('resolves to custom for agent with Moonshot provider', () => { + mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }; + mockAgentsMap = { + 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial, + }; + const { result } = renderHook(() => useDragDropContext(), { wrapper }); + expect(result.current.endpointType).toBe(EModelEndpoint.custom); + }); + + it('resolves to custom for agent with custom provider with spaces', () => { + mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }; + mockAgentsMap = { + 'agent-1': { provider: 'Some Endpoint', model_parameters: {} } as Partial, + }; + const { result } = renderHook(() => useDragDropContext(), { wrapper }); + expect(result.current.endpointType).toBe(EModelEndpoint.custom); + }); + + it('resolves to openAI for agent with openAI provider', () => { + mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }; + mockAgentsMap = { + 'agent-1': { provider: EModelEndpoint.openAI, model_parameters: {} } as Partial, + }; + const { result } = renderHook(() => useDragDropContext(), { wrapper }); + expect(result.current.endpointType).toBe(EModelEndpoint.openAI); + }); + + it('resolves to anthropic for agent with anthropic provider', () => { + mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }; + mockAgentsMap = { + 'agent-1': { provider: EModelEndpoint.anthropic, model_parameters: {} } as Partial, + }; + const { result } = renderHook(() => useDragDropContext(), { wrapper }); + expect(result.current.endpointType).toBe(EModelEndpoint.anthropic); + }); + }); + + describe('agents endpoint with provider from agentData query', () => { + it('uses agentData when agent is not in agentsMap', () => { + mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-2' }; + mockAgentsMap = {}; + mockAgentQueryData = { provider: 'Moonshot' } as Partial; + const { result } = renderHook(() => useDragDropContext(), { wrapper }); + expect(result.current.endpointType).toBe(EModelEndpoint.custom); + }); + }); + + describe('agents endpoint without provider', () => { + it('falls back to agents when no agent_id', () => { + mockConversation = { endpoint: EModelEndpoint.agents }; + const { result } = renderHook(() => useDragDropContext(), { wrapper }); + expect(result.current.endpointType).toBe(EModelEndpoint.agents); + }); + + it('falls back to agents when agent has no provider', () => { + mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }; + mockAgentsMap = { 'agent-1': { model_parameters: {} } as Partial }; + const { result } = renderHook(() => useDragDropContext(), { wrapper }); + expect(result.current.endpointType).toBe(EModelEndpoint.agents); + }); + }); + + describe('consistency: same endpoint type whether used directly or through agents', () => { + it('Moonshot resolves to the same type as direct endpoint and as agent provider', () => { + mockConversation = { endpoint: 'Moonshot' }; + const { result: directResult } = renderHook(() => useDragDropContext(), { wrapper }); + + mockConversation = { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }; + mockAgentsMap = { + 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial, + }; + const { result: agentResult } = renderHook(() => useDragDropContext(), { wrapper }); + + expect(directResult.current.endpointType).toBe(agentResult.current.endpointType); + }); + }); +}); diff --git a/client/src/a11y/LiveAnnouncer.tsx b/client/src/a11y/LiveAnnouncer.tsx index 9a02711556..0eac8089bc 100644 --- a/client/src/a11y/LiveAnnouncer.tsx +++ b/client/src/a11y/LiveAnnouncer.tsx @@ -56,10 +56,13 @@ const LiveAnnouncer: React.FC = ({ children }) => { const announceAssertive = announcePolite; - const contextValue = { - announcePolite, - announceAssertive, - }; + const contextValue = useMemo( + () => ({ + announcePolite, + announceAssertive, + }), + [announcePolite, announceAssertive], + ); useEffect(() => { return () => { diff --git a/client/src/components/Agents/tests/VirtualScrollingPerformance.test.tsx b/client/src/components/Agents/tests/VirtualScrollingPerformance.test.tsx index 1e1b7d1e4b..293bd8878e 100644 --- a/client/src/components/Agents/tests/VirtualScrollingPerformance.test.tsx +++ b/client/src/components/Agents/tests/VirtualScrollingPerformance.test.tsx @@ -179,9 +179,7 @@ describe('Virtual Scrolling Performance', () => { }; it('efficiently handles 1000 agents without rendering all DOM nodes', () => { - const startTime = performance.now(); renderComponent(1000); - const endTime = performance.now(); const virtualList = screen.getByTestId('virtual-list'); expect(virtualList).toBeInTheDocument(); @@ -191,19 +189,10 @@ describe('Virtual Scrolling Performance', () => { const renderedCards = screen.getAllByTestId(/agent-card-/); expect(renderedCards.length).toBeLessThan(50); // Much less than 1000 expect(renderedCards.length).toBeGreaterThan(0); - - // Performance check: rendering should be fast - const renderTime = endTime - startTime; - expect(renderTime).toBeLessThan(740); - - console.log(`Rendered 1000 agents in ${renderTime.toFixed(2)}ms`); - console.log(`Only ${renderedCards.length} DOM nodes created for 1000 agents`); }); it('efficiently handles 5000 agents (stress test)', () => { - const startTime = performance.now(); renderComponent(5000); - const endTime = performance.now(); const virtualList = screen.getByTestId('virtual-list'); expect(virtualList).toBeInTheDocument(); @@ -213,13 +202,6 @@ describe('Virtual Scrolling Performance', () => { const renderedCards = screen.getAllByTestId(/agent-card-/); expect(renderedCards.length).toBeLessThan(50); expect(renderedCards.length).toBeGreaterThan(0); - - // Performance should still be reasonable - const renderTime = endTime - startTime; - expect(renderTime).toBeLessThan(200); // Should render in less than 200ms - - console.log(`Rendered 5000 agents in ${renderTime.toFixed(2)}ms`); - console.log(`Only ${renderedCards.length} DOM nodes created for 5000 agents`); }); it('calculates correct number of virtual rows for different screen sizes', () => { diff --git a/client/src/components/Artifacts/ArtifactCodeEditor.tsx b/client/src/components/Artifacts/ArtifactCodeEditor.tsx index 4ab2b182b8..d03397821d 100644 --- a/client/src/components/Artifacts/ArtifactCodeEditor.tsx +++ b/client/src/components/Artifacts/ArtifactCodeEditor.tsx @@ -1,206 +1,326 @@ -import React, { useMemo, useState, useEffect, useRef, memo } from 'react'; +import React, { useMemo, useState, useEffect, useRef, useCallback } from 'react'; import debounce from 'lodash/debounce'; -import { KeyBinding } from '@codemirror/view'; -import { autocompletion, completionKeymap } from '@codemirror/autocomplete'; -import { - useSandpack, - SandpackCodeEditor, - SandpackProvider as StyledProvider, -} from '@codesandbox/sandpack-react'; -import type { SandpackProviderProps } from '@codesandbox/sandpack-react/unstyled'; -import type { SandpackBundlerFile } from '@codesandbox/sandpack-client'; -import type { CodeEditorRef } from '@codesandbox/sandpack-react'; -import type { ArtifactFiles, Artifact } from '~/common'; -import { useEditArtifact, useGetStartupConfig } from '~/data-provider'; +import MonacoEditor from '@monaco-editor/react'; +import type { Monaco } from '@monaco-editor/react'; +import type { editor } from 'monaco-editor'; +import type { Artifact } from '~/common'; import { useMutationState, useCodeState } from '~/Providers/EditorContext'; import { useArtifactsContext } from '~/Providers'; -import { sharedFiles, sharedOptions } from '~/utils/artifacts'; +import { useEditArtifact } from '~/data-provider'; -const CodeEditor = memo( - ({ - fileKey, - readOnly, - artifact, - editorRef, - }: { - fileKey: string; - readOnly?: boolean; - artifact: Artifact; - editorRef: React.MutableRefObject; - }) => { - const { sandpack } = useSandpack(); - const [currentUpdate, setCurrentUpdate] = useState(null); - const { isMutating, setIsMutating } = useMutationState(); - const { setCurrentCode } = useCodeState(); - const editArtifact = useEditArtifact({ - onMutate: (vars) => { - setIsMutating(true); - setCurrentUpdate(vars.updated); - }, - onSuccess: () => { - setIsMutating(false); - setCurrentUpdate(null); - }, - onError: () => { - setIsMutating(false); - }, - }); +const LANG_MAP: Record = { + javascript: 'javascript', + typescript: 'typescript', + python: 'python', + css: 'css', + json: 'json', + markdown: 'markdown', + html: 'html', + xml: 'xml', + sql: 'sql', + yaml: 'yaml', + shell: 'shell', + bash: 'shell', + tsx: 'typescript', + jsx: 'javascript', + c: 'c', + cpp: 'cpp', + java: 'java', + go: 'go', + rust: 'rust', + kotlin: 'kotlin', + swift: 'swift', + php: 'php', + ruby: 'ruby', + r: 'r', + lua: 'lua', + scala: 'scala', + perl: 'perl', +}; - /** - * Create stable debounced mutation that doesn't depend on changing callbacks - * Use refs to always access the latest values without recreating the debounce - */ - const artifactRef = useRef(artifact); - const isMutatingRef = useRef(isMutating); - const currentUpdateRef = useRef(currentUpdate); - const editArtifactRef = useRef(editArtifact); - const setCurrentCodeRef = useRef(setCurrentCode); +const TYPE_MAP: Record = { + 'text/html': 'html', + 'application/vnd.code-html': 'html', + 'application/vnd.react': 'typescript', + 'application/vnd.ant.react': 'typescript', + 'text/markdown': 'markdown', + 'text/md': 'markdown', + 'text/plain': 'plaintext', + 'application/vnd.mermaid': 'markdown', +}; - useEffect(() => { - artifactRef.current = artifact; - }, [artifact]); +function getMonacoLanguage(type?: string, language?: string): string { + if (language && LANG_MAP[language]) { + return LANG_MAP[language]; + } + return TYPE_MAP[type ?? ''] ?? 'plaintext'; +} - useEffect(() => { - isMutatingRef.current = isMutating; - }, [isMutating]); - - useEffect(() => { - currentUpdateRef.current = currentUpdate; - }, [currentUpdate]); - - useEffect(() => { - editArtifactRef.current = editArtifact; - }, [editArtifact]); - - useEffect(() => { - setCurrentCodeRef.current = setCurrentCode; - }, [setCurrentCode]); - - /** - * Create debounced mutation once - never recreate it - * All values are accessed via refs so they're always current - */ - const debouncedMutation = useMemo( - () => - debounce((code: string) => { - if (readOnly) { - return; - } - if (isMutatingRef.current) { - return; - } - if (artifactRef.current.index == null) { - return; - } - - const artifact = artifactRef.current; - const artifactIndex = artifact.index; - const isNotOriginal = - code && artifact.content != null && code.trim() !== artifact.content.trim(); - const isNotRepeated = - currentUpdateRef.current == null - ? true - : code != null && code.trim() !== currentUpdateRef.current.trim(); - - if (artifact.content && isNotOriginal && isNotRepeated && artifactIndex != null) { - setCurrentCodeRef.current(code); - editArtifactRef.current.mutate({ - index: artifactIndex, - messageId: artifact.messageId ?? '', - original: artifact.content, - updated: code, - }); - } - }, 500), - [readOnly], - ); - - /** - * Listen to Sandpack file changes and trigger debounced mutation - */ - useEffect(() => { - const currentCode = (sandpack.files['/' + fileKey] as SandpackBundlerFile | undefined)?.code; - if (currentCode) { - debouncedMutation(currentCode); - } - }, [sandpack.files, fileKey, debouncedMutation]); - - /** - * Cleanup: cancel pending mutations when component unmounts or artifact changes - */ - useEffect(() => { - return () => { - debouncedMutation.cancel(); - }; - }, [artifact.id, debouncedMutation]); - - return ( - (completionKeymap)} - className="hljs language-javascript bg-black" - /> - ); - }, -); - -export const ArtifactCodeEditor = function ({ - files, - fileKey, - template, +export const ArtifactCodeEditor = function ArtifactCodeEditor({ artifact, - editorRef, - sharedProps, + monacoRef, readOnly: externalReadOnly, }: { - fileKey: string; artifact: Artifact; - files: ArtifactFiles; - template: SandpackProviderProps['template']; - sharedProps: Partial; - editorRef: React.MutableRefObject; + monacoRef: React.MutableRefObject; readOnly?: boolean; }) { - const { data: config } = useGetStartupConfig(); const { isSubmitting } = useArtifactsContext(); - const options: typeof sharedOptions = useMemo(() => { - if (!config) { - return sharedOptions; - } - return { - ...sharedOptions, - activeFile: '/' + fileKey, - bundlerURL: template === 'static' ? config.staticBundlerURL : config.bundlerURL, - }; - }, [config, template, fileKey]); - const initialReadOnly = (externalReadOnly ?? false) || (isSubmitting ?? false); - const [readOnly, setReadOnly] = useState(initialReadOnly); - useEffect(() => { - setReadOnly((externalReadOnly ?? false) || (isSubmitting ?? false)); - }, [isSubmitting, externalReadOnly]); + const readOnly = (externalReadOnly ?? false) || isSubmitting; + const { setCurrentCode } = useCodeState(); + const [currentUpdate, setCurrentUpdate] = useState(null); + const { isMutating, setIsMutating } = useMutationState(); + const editArtifact = useEditArtifact({ + onMutate: (vars) => { + setIsMutating(true); + setCurrentUpdate(vars.updated); + }, + onSuccess: () => { + setIsMutating(false); + setCurrentUpdate(null); + }, + onError: () => { + setIsMutating(false); + }, + }); - if (Object.keys(files).length === 0) { + const artifactRef = useRef(artifact); + const isMutatingRef = useRef(isMutating); + const currentUpdateRef = useRef(currentUpdate); + const editArtifactRef = useRef(editArtifact); + const setCurrentCodeRef = useRef(setCurrentCode); + const prevContentRef = useRef(artifact.content ?? ''); + const prevArtifactId = useRef(artifact.id); + const prevReadOnly = useRef(readOnly); + + artifactRef.current = artifact; + isMutatingRef.current = isMutating; + currentUpdateRef.current = currentUpdate; + editArtifactRef.current = editArtifact; + setCurrentCodeRef.current = setCurrentCode; + + const debouncedMutation = useMemo( + () => + debounce((code: string) => { + if (readOnly || isMutatingRef.current || artifactRef.current.index == null) { + return; + } + const art = artifactRef.current; + const isNotOriginal = art.content != null && code.trim() !== art.content.trim(); + const isNotRepeated = + currentUpdateRef.current == null ? true : code.trim() !== currentUpdateRef.current.trim(); + + if (art.content != null && isNotOriginal && isNotRepeated && art.index != null) { + setCurrentCodeRef.current(code); + editArtifactRef.current.mutate({ + index: art.index, + messageId: art.messageId ?? '', + original: art.content, + updated: code, + }); + } + }, 500), + [readOnly], + ); + + useEffect(() => { + return () => debouncedMutation.cancel(); + }, [artifact.id, debouncedMutation]); + + /** + * Streaming: use model.applyEdits() to append new content. + * Unlike setValue/pushEditOperations, applyEdits preserves existing + * tokens so syntax highlighting doesn't flash during updates. + */ + useEffect(() => { + const ed = monacoRef.current; + if (!ed || !readOnly) { + return; + } + const newContent = artifact.content ?? ''; + const prev = prevContentRef.current; + + if (newContent === prev) { + return; + } + + const model = ed.getModel(); + if (!model) { + return; + } + + if (newContent.startsWith(prev) && prev.length > 0) { + const appended = newContent.slice(prev.length); + const endPos = model.getPositionAt(model.getValueLength()); + model.applyEdits([ + { + range: { + startLineNumber: endPos.lineNumber, + startColumn: endPos.column, + endLineNumber: endPos.lineNumber, + endColumn: endPos.column, + }, + text: appended, + }, + ]); + } else { + model.setValue(newContent); + } + + prevContentRef.current = newContent; + ed.revealLine(model.getLineCount()); + }, [artifact.content, readOnly, monacoRef]); + + useEffect(() => { + if (artifact.id === prevArtifactId.current) { + return; + } + prevArtifactId.current = artifact.id; + prevContentRef.current = artifact.content ?? ''; + const ed = monacoRef.current; + if (ed && artifact.content != null) { + ed.getModel()?.setValue(artifact.content); + } + }, [artifact.id, artifact.content, monacoRef]); + + useEffect(() => { + if (prevReadOnly.current && !readOnly && artifact.content != null) { + const ed = monacoRef.current; + if (ed) { + ed.getModel()?.setValue(artifact.content); + prevContentRef.current = artifact.content; + } + } + prevReadOnly.current = readOnly; + }, [readOnly, artifact.content, monacoRef]); + + const handleChange = useCallback( + (value: string | undefined) => { + if (value === undefined || readOnly) { + return; + } + prevContentRef.current = value; + setCurrentCode(value); + if (value.length > 0) { + debouncedMutation(value); + } + }, + [readOnly, debouncedMutation, setCurrentCode], + ); + + /** + * Disable all validation — this is an artifact viewer/editor, not an IDE. + * Note: these are global Monaco settings that affect all editor instances on the page. + * The `as unknown` cast is required because monaco-editor v0.55 types `.languages.typescript` + * as `{ deprecated: true }` while the runtime API is fully functional. + */ + const handleBeforeMount = useCallback((monaco: Monaco) => { + const { typescriptDefaults, javascriptDefaults, JsxEmit } = monaco.languages + .typescript as unknown as { + typescriptDefaults: { + setDiagnosticsOptions: (o: { + noSemanticValidation: boolean; + noSyntaxValidation: boolean; + }) => void; + setCompilerOptions: (o: { + allowNonTsExtensions: boolean; + allowJs: boolean; + jsx: number; + }) => void; + }; + javascriptDefaults: { + setDiagnosticsOptions: (o: { + noSemanticValidation: boolean; + noSyntaxValidation: boolean; + }) => void; + setCompilerOptions: (o: { + allowNonTsExtensions: boolean; + allowJs: boolean; + jsx: number; + }) => void; + }; + JsxEmit: { React: number }; + }; + const diagnosticsOff = { noSemanticValidation: true, noSyntaxValidation: true }; + const compilerBase = { allowNonTsExtensions: true, allowJs: true, jsx: JsxEmit.React }; + typescriptDefaults.setDiagnosticsOptions(diagnosticsOff); + javascriptDefaults.setDiagnosticsOptions(diagnosticsOff); + typescriptDefaults.setCompilerOptions(compilerBase); + javascriptDefaults.setCompilerOptions(compilerBase); + }, []); + + const handleMount = useCallback( + (ed: editor.IStandaloneCodeEditor) => { + monacoRef.current = ed; + prevContentRef.current = ed.getModel()?.getValue() ?? artifact.content ?? ''; + if (readOnly) { + const model = ed.getModel(); + if (model) { + ed.revealLine(model.getLineCount()); + } + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [monacoRef], + ); + + const language = getMonacoLanguage(artifact.type, artifact.language); + + const editorOptions = useMemo( + () => ({ + readOnly, + minimap: { enabled: false }, + lineNumbers: 'on', + scrollBeyondLastLine: false, + fontSize: 13, + tabSize: 2, + wordWrap: 'on', + automaticLayout: true, + padding: { top: 8 }, + renderLineHighlight: readOnly ? 'none' : 'line', + cursorStyle: readOnly ? 'underline-thin' : 'line', + scrollbar: { + vertical: 'visible', + horizontal: 'auto', + verticalScrollbarSize: 8, + horizontalScrollbarSize: 8, + useShadows: false, + alwaysConsumeMouseWheel: false, + }, + overviewRulerLanes: 0, + hideCursorInOverviewRuler: true, + overviewRulerBorder: false, + folding: false, + glyphMargin: false, + colorDecorators: !readOnly, + occurrencesHighlight: readOnly ? 'off' : 'singleFile', + selectionHighlight: !readOnly, + renderValidationDecorations: readOnly ? 'off' : 'editable', + quickSuggestions: !readOnly, + suggestOnTriggerCharacters: !readOnly, + parameterHints: { enabled: !readOnly }, + hover: { enabled: !readOnly }, + matchBrackets: readOnly ? 'never' : 'always', + }), + [readOnly], + ); + + if (!artifact.content) { return null; } return ( - - - +
+ +
); }; diff --git a/client/src/components/Artifacts/ArtifactTabs.tsx b/client/src/components/Artifacts/ArtifactTabs.tsx index 8e2a92eb9c..32332215f0 100644 --- a/client/src/components/Artifacts/ArtifactTabs.tsx +++ b/client/src/components/Artifacts/ArtifactTabs.tsx @@ -1,30 +1,26 @@ import { useRef, useEffect } from 'react'; import * as Tabs from '@radix-ui/react-tabs'; import type { SandpackPreviewRef } from '@codesandbox/sandpack-react/unstyled'; -import type { CodeEditorRef } from '@codesandbox/sandpack-react'; +import type { editor } from 'monaco-editor'; import type { Artifact } from '~/common'; import { useCodeState } from '~/Providers/EditorContext'; -import { useArtifactsContext } from '~/Providers'; import useArtifactProps from '~/hooks/Artifacts/useArtifactProps'; -import { useAutoScroll } from '~/hooks/Artifacts/useAutoScroll'; import { ArtifactCodeEditor } from './ArtifactCodeEditor'; import { useGetStartupConfig } from '~/data-provider'; import { ArtifactPreview } from './ArtifactPreview'; export default function ArtifactTabs({ artifact, - editorRef, previewRef, isSharedConvo, }: { artifact: Artifact; - editorRef: React.MutableRefObject; previewRef: React.MutableRefObject; isSharedConvo?: boolean; }) { - const { isSubmitting } = useArtifactsContext(); const { currentCode, setCurrentCode } = useCodeState(); const { data: startupConfig } = useGetStartupConfig(); + const monacoRef = useRef(null); const lastIdRef = useRef(null); useEffect(() => { @@ -34,33 +30,24 @@ export default function ArtifactTabs({ lastIdRef.current = artifact.id; }, [setCurrentCode, artifact.id]); - const content = artifact.content ?? ''; - const contentRef = useRef(null); - useAutoScroll({ ref: contentRef, content, isSubmitting }); - const { files, fileKey, template, sharedProps } = useArtifactProps({ artifact }); return (
- + - + (); const previewRef = useRef(); const [isVisible, setIsVisible] = useState(false); const [isClosing, setIsClosing] = useState(false); @@ -297,7 +296,6 @@ export default function Artifacts() {
} previewRef={previewRef as React.MutableRefObject} isSharedConvo={isSharedConvo} /> diff --git a/client/src/components/Artifacts/Code.tsx b/client/src/components/Artifacts/Code.tsx index 6894ce775b..001b010908 100644 --- a/client/src/components/Artifacts/Code.tsx +++ b/client/src/components/Artifacts/Code.tsx @@ -1,11 +1,8 @@ -import React, { memo, useEffect, useRef, useState } from 'react'; +import React, { memo, useState } from 'react'; import copy from 'copy-to-clipboard'; -import rehypeKatex from 'rehype-katex'; -import ReactMarkdown from 'react-markdown'; import { Button } from '@librechat/client'; -import rehypeHighlight from 'rehype-highlight'; import { Copy, CircleCheckBig } from 'lucide-react'; -import { handleDoubleClick, langSubset } from '~/utils'; +import { handleDoubleClick } from '~/utils'; import { useLocalize } from '~/hooks'; type TCodeProps = { @@ -29,74 +26,6 @@ export const code: React.ElementType = memo(({ inline, className, children }: TC return {children}; }); -export const CodeMarkdown = memo( - ({ content = '', isSubmitting }: { content: string; isSubmitting: boolean }) => { - const scrollRef = useRef(null); - const [userScrolled, setUserScrolled] = useState(false); - const currentContent = content; - const rehypePlugins = [ - [rehypeKatex], - [ - rehypeHighlight, - { - detect: true, - ignoreMissing: true, - subset: langSubset, - }, - ], - ]; - - useEffect(() => { - const scrollContainer = scrollRef.current; - if (!scrollContainer) { - return; - } - - const handleScroll = () => { - const { scrollTop, scrollHeight, clientHeight } = scrollContainer; - const isNearBottom = scrollHeight - scrollTop - clientHeight < 50; - - if (!isNearBottom) { - setUserScrolled(true); - } else { - setUserScrolled(false); - } - }; - - scrollContainer.addEventListener('scroll', handleScroll); - - return () => { - scrollContainer.removeEventListener('scroll', handleScroll); - }; - }, []); - - useEffect(() => { - const scrollContainer = scrollRef.current; - if (!scrollContainer || !isSubmitting || userScrolled) { - return; - } - - scrollContainer.scrollTop = scrollContainer.scrollHeight; - }, [content, isSubmitting, userScrolled]); - - return ( -
- - {currentContent} - -
- ); - }, -); - export const CopyCodeButton: React.FC<{ content: string }> = ({ content }) => { const localize = useLocalize(); const [isCopied, setIsCopied] = useState(false); diff --git a/client/src/components/Artifacts/Mermaid.tsx b/client/src/components/Artifacts/Mermaid.tsx index f7291998a4..5eb55be3ae 100644 --- a/client/src/components/Artifacts/Mermaid.tsx +++ b/client/src/components/Artifacts/Mermaid.tsx @@ -1,153 +1,123 @@ -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState, useCallback } from 'react'; import mermaid from 'mermaid'; import { Button } from '@librechat/client'; -import { TransformWrapper, TransformComponent, ReactZoomPanPinchRef } from 'react-zoom-pan-pinch'; -import { ZoomIn, ZoomOut, RefreshCw } from 'lucide-react'; +import { ZoomIn, ZoomOut, RotateCcw } from 'lucide-react'; +import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch'; +import type { ReactZoomPanPinchRef } from 'react-zoom-pan-pinch'; +import { artifactFlowchartConfig } from '~/utils/mermaid'; interface MermaidDiagramProps { content: string; + isDarkMode?: boolean; } -/** Note: this is just for testing purposes, don't actually use this component */ -const MermaidDiagram: React.FC = ({ content }) => { +const MermaidDiagram: React.FC = ({ content, isDarkMode = true }) => { const mermaidRef = useRef(null); const transformRef = useRef(null); const [isRendered, setIsRendered] = useState(false); + const theme = isDarkMode ? 'dark' : 'neutral'; + const bgColor = isDarkMode ? '#212121' : '#FFFFFF'; useEffect(() => { mermaid.initialize({ startOnLoad: false, - theme: 'base', + theme, securityLevel: 'sandbox', - themeVariables: { - background: '#282C34', - primaryColor: '#333842', - secondaryColor: '#333842', - tertiaryColor: '#333842', - primaryTextColor: '#ABB2BF', - secondaryTextColor: '#ABB2BF', - lineColor: '#636D83', - fontSize: '16px', - nodeBorder: '#636D83', - mainBkg: '#282C34', - altBackground: '#282C34', - textColor: '#ABB2BF', - edgeLabelBackground: '#282C34', - clusterBkg: '#282C34', - clusterBorder: '#636D83', - labelBoxBkgColor: '#333842', - labelBoxBorderColor: '#636D83', - labelTextColor: '#ABB2BF', - }, - flowchart: { - curve: 'basis', - nodeSpacing: 50, - rankSpacing: 50, - diagramPadding: 8, - htmlLabels: true, - useMaxWidth: true, - padding: 15, - wrappingWidth: 200, - }, + flowchart: artifactFlowchartConfig, }); const renderDiagram = async () => { - if (mermaidRef.current) { - try { - const { svg } = await mermaid.render('mermaid-diagram', content); - mermaidRef.current.innerHTML = svg; + if (!mermaidRef.current) { + return; + } - const svgElement = mermaidRef.current.querySelector('svg'); - if (svgElement) { - svgElement.style.width = '100%'; - svgElement.style.height = '100%'; + try { + const { svg } = await mermaid.render('mermaid-diagram', content); + mermaidRef.current.innerHTML = svg; - const pathElements = svgElement.querySelectorAll('path'); - pathElements.forEach((path) => { - path.style.strokeWidth = '1.5px'; - }); - - const rectElements = svgElement.querySelectorAll('rect'); - rectElements.forEach((rect) => { - const parent = rect.parentElement; - if (parent && parent.classList.contains('node')) { - rect.style.stroke = '#636D83'; - rect.style.strokeWidth = '1px'; - } else { - rect.style.stroke = 'none'; - } - }); - } - setIsRendered(true); - } catch (error) { - console.error('Mermaid rendering error:', error); + const svgElement = mermaidRef.current.querySelector('svg'); + if (svgElement) { + svgElement.style.width = '100%'; + svgElement.style.height = '100%'; + } + setIsRendered(true); + } catch (error) { + console.error('Mermaid rendering error:', error); + if (mermaidRef.current) { mermaidRef.current.innerHTML = 'Error rendering diagram'; } } }; renderDiagram(); - }, [content]); + }, [content, theme]); - const centerAndFitDiagram = () => { + const centerAndFitDiagram = useCallback(() => { if (transformRef.current && mermaidRef.current) { const { centerView, zoomToElement } = transformRef.current; zoomToElement(mermaidRef.current as HTMLElement); centerView(1, 0); } - }; + }, []); useEffect(() => { if (isRendered) { centerAndFitDiagram(); } - }, [isRendered]); + }, [isRendered, centerAndFitDiagram]); - const handlePanning = () => { - if (transformRef.current) { - const { state, instance } = (transformRef.current as ReactZoomPanPinchRef | undefined) ?? {}; - if (!state || !instance) { - return; - } - const { scale, positionX, positionY } = state; - const { wrapperComponent, contentComponent } = instance; - - if (wrapperComponent && contentComponent) { - const wrapperRect = wrapperComponent.getBoundingClientRect(); - const contentRect = contentComponent.getBoundingClientRect(); - const maxX = wrapperRect.width - contentRect.width * scale; - const maxY = wrapperRect.height - contentRect.height * scale; - - let newX = positionX; - let newY = positionY; - - if (newX > 0) { - newX = 0; - } - if (newY > 0) { - newY = 0; - } - if (newX < maxX) { - newX = maxX; - } - if (newY < maxY) { - newY = maxY; - } - - if (newX !== positionX || newY !== positionY) { - instance.setTransformState(scale, newX, newY); - } - } + const handlePanning = useCallback(() => { + if (!transformRef.current) { + return; } - }; + + const { state, instance } = transformRef.current; + if (!state || !instance) { + return; + } + const { scale, positionX, positionY } = state; + const { wrapperComponent, contentComponent } = instance; + + if (!wrapperComponent || !contentComponent) { + return; + } + + const wrapperRect = wrapperComponent.getBoundingClientRect(); + const contentRect = contentComponent.getBoundingClientRect(); + const maxX = wrapperRect.width - contentRect.width * scale; + const maxY = wrapperRect.height - contentRect.height * scale; + + let newX = positionX; + let newY = positionY; + + if (newX > 0) { + newX = 0; + } + if (newY > 0) { + newY = 0; + } + if (newX < maxX) { + newX = maxX; + } + if (newY < maxY) { + newY = maxY; + } + + if (newX !== positionX || newY !== positionY) { + instance.setTransformState(scale, newX, newY); + } + }, []); return ( -
+
= ({ content }) => {
diff --git a/client/src/components/Auth/Login.tsx b/client/src/components/Auth/Login.tsx index 48a506879f..7c3adf51bd 100644 --- a/client/src/components/Auth/Login.tsx +++ b/client/src/components/Auth/Login.tsx @@ -1,15 +1,19 @@ import { useEffect, useState } from 'react'; import { ErrorTypes, registerPage } from 'librechat-data-provider'; import { OpenIDIcon, useToastContext } from '@librechat/client'; -import { useOutletContext, useSearchParams } from 'react-router-dom'; +import { useOutletContext, useSearchParams, useLocation } from 'react-router-dom'; import type { TLoginLayoutContext } from '~/common'; +import { getLoginError, persistRedirectToSession } from '~/utils'; import { ErrorMessage } from '~/components/Auth/ErrorMessage'; import SocialButton from '~/components/Auth/SocialButton'; import { useAuthContext } from '~/hooks/AuthContext'; -import { getLoginError } from '~/utils'; import { useLocalize } from '~/hooks'; import LoginForm from './LoginForm'; +interface LoginLocationState { + redirect_to?: string; +} + function Login() { const localize = useLocalize(); const { showToast } = useToastContext(); @@ -17,13 +21,22 @@ function Login() { const { startupConfig } = useOutletContext(); const [searchParams, setSearchParams] = useSearchParams(); - // Determine if auto-redirect should be disabled based on the URL parameter + const location = useLocation(); const disableAutoRedirect = searchParams.get('redirect') === 'false'; - // Persist the disable flag locally so that once detected, auto-redirect stays disabled. const [isAutoRedirectDisabled, setIsAutoRedirectDisabled] = useState(disableAutoRedirect); useEffect(() => { + const redirectTo = searchParams.get('redirect_to'); + if (redirectTo) { + persistRedirectToSession(redirectTo); + } else { + const state = location.state as LoginLocationState | null; + if (state?.redirect_to) { + persistRedirectToSession(state.redirect_to); + } + } + const oauthError = searchParams?.get('error'); if (oauthError && oauthError === ErrorTypes.AUTH_FAILED) { showToast({ @@ -34,9 +47,8 @@ function Login() { newParams.delete('error'); setSearchParams(newParams, { replace: true }); } - }, [searchParams, setSearchParams, showToast, localize]); + }, [searchParams, setSearchParams, showToast, localize, location.state]); - // Once the disable flag is detected, update local state and remove the parameter from the URL. useEffect(() => { if (disableAutoRedirect) { setIsAutoRedirectDisabled(true); @@ -46,7 +58,6 @@ function Login() { } }, [disableAutoRedirect, searchParams, setSearchParams]); - // Determine whether we should auto-redirect to OpenID. const shouldAutoRedirect = startupConfig?.openidLoginEnabled && startupConfig?.openidAutoRedirect && @@ -60,7 +71,6 @@ function Login() { } }, [shouldAutoRedirect, startupConfig]); - // Render fallback UI if auto-redirect is active. if (shouldAutoRedirect) { return (
diff --git a/client/src/components/Chat/AddMultiConvo.tsx b/client/src/components/Chat/AddMultiConvo.tsx index 7cabe0f336..48e9919092 100644 --- a/client/src/components/Chat/AddMultiConvo.tsx +++ b/client/src/components/Chat/AddMultiConvo.tsx @@ -1,17 +1,21 @@ +import { useCallback } from 'react'; +import { useSetRecoilState, useRecoilValue } from 'recoil'; import { PlusCircle } from 'lucide-react'; import { TooltipAnchor } from '@librechat/client'; import { isAssistantsEndpoint } from 'librechat-data-provider'; import type { TConversation } from 'librechat-data-provider'; -import { useChatContext, useAddedChatContext } from '~/Providers'; +import { useGetConversation, useLocalize } from '~/hooks'; import { mainTextareaId } from '~/common'; -import { useLocalize } from '~/hooks'; +import store from '~/store'; function AddMultiConvo() { - const { conversation } = useChatContext(); - const { setConversation: setAddedConvo } = useAddedChatContext(); const localize = useLocalize(); + const getConversation = useGetConversation(0); + const endpoint = useRecoilValue(store.conversationEndpointByIndex(0)); + const setAddedConvo = useSetRecoilState(store.conversationByIndex(1)); - const clickHandler = () => { + const clickHandler = useCallback(() => { + const conversation = getConversation(); const { title: _t, ...convo } = conversation ?? ({} as TConversation); setAddedConvo({ ...convo, @@ -22,13 +26,13 @@ function AddMultiConvo() { if (textarea) { textarea.focus(); } - }; + }, [getConversation, setAddedConvo]); - if (!conversation) { + if (!endpoint) { return null; } - if (isAssistantsEndpoint(conversation.endpoint)) { + if (isAssistantsEndpoint(endpoint)) { return null; } diff --git a/client/src/components/Chat/Footer.tsx b/client/src/components/Chat/Footer.tsx index 75dd853c4f..541647a8d0 100644 --- a/client/src/components/Chat/Footer.tsx +++ b/client/src/components/Chat/Footer.tsx @@ -1,11 +1,11 @@ -import React, { useEffect } from 'react'; -import ReactMarkdown from 'react-markdown'; +import React, { useEffect, memo } from 'react'; import TagManager from 'react-gtm-module'; +import ReactMarkdown from 'react-markdown'; import { Constants } from 'librechat-data-provider'; import { useGetStartupConfig } from '~/data-provider'; import { useLocalize } from '~/hooks'; -export default function Footer({ className }: { className?: string }) { +function Footer({ className }: { className?: string }) { const { data: config } = useGetStartupConfig(); const localize = useLocalize(); @@ -98,3 +98,8 @@ export default function Footer({ className }: { className?: string }) {
); } + +const MemoizedFooter = memo(Footer); +MemoizedFooter.displayName = 'Footer'; + +export default MemoizedFooter; diff --git a/client/src/components/Chat/Header.tsx b/client/src/components/Chat/Header.tsx index 40e2c6b7ad..9e44e804c9 100644 --- a/client/src/components/Chat/Header.tsx +++ b/client/src/components/Chat/Header.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { memo, useMemo } from 'react'; import { useMediaQuery } from '@librechat/client'; import { useOutletContext } from 'react-router-dom'; import { AnimatePresence, motion } from 'framer-motion'; @@ -16,7 +16,7 @@ import { cn } from '~/utils'; const defaultInterface = getConfigDefaults().interface; -export default function Header() { +function Header() { const { data: startupConfig } = useGetStartupConfig(); const { navVisible, setNavVisible } = useOutletContext(); @@ -35,6 +35,11 @@ export default function Header() { permission: Permissions.USE, }); + const hasAccessToTemporaryChat = useHasAccess({ + permissionType: PermissionTypes.TEMPORARY_CHAT, + permission: Permissions.USE, + }); + const isSmallScreen = useMediaQuery('(max-width: 768px)'); return ( @@ -73,7 +78,7 @@ export default function Header() { - + {hasAccessToTemporaryChat === true && } )}
@@ -85,7 +90,7 @@ export default function Header() { - + {hasAccessToTemporaryChat === true && }
)}
@@ -94,3 +99,8 @@ export default function Header() { ); } + +const MemoizedHeader = memo(Header); +MemoizedHeader.displayName = 'Header'; + +export default MemoizedHeader; diff --git a/client/src/components/Chat/Input/BadgeRow.tsx b/client/src/components/Chat/Input/BadgeRow.tsx index 5036dcd5e4..6fea6b0d58 100644 --- a/client/src/components/Chat/Input/BadgeRow.tsx +++ b/client/src/components/Chat/Input/BadgeRow.tsx @@ -28,6 +28,7 @@ interface BadgeRowProps { onChange: (badges: Pick[]) => void; onToggle?: (badgeId: string, currentActive: boolean) => void; conversationId?: string | null; + specName?: string | null; isSubmitting?: boolean; isInChat: boolean; } @@ -142,6 +143,7 @@ const dragReducer = (state: DragState, action: DragAction): DragState => { function BadgeRow({ showEphemeralBadges, conversationId, + specName, isSubmitting, onChange, onToggle, @@ -320,7 +322,11 @@ function BadgeRow({ }, [dragState.draggedBadge, handleMouseMove, handleMouseUp]); return ( - +
{showEphemeralBadges === true && } {tempBadges.map((badge, index) => ( diff --git a/client/src/components/Chat/Input/ChatForm.tsx b/client/src/components/Chat/Input/ChatForm.tsx index c1ea69ee67..8a0f3ec02c 100644 --- a/client/src/components/Chat/Input/ChatForm.tsx +++ b/client/src/components/Chat/Input/ChatForm.tsx @@ -197,7 +197,7 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => { const baseClasses = useMemo( () => cn( - 'md:py-3.5 m-0 w-full resize-none py-[13px] placeholder-black/50 bg-transparent dark:placeholder-white/50 [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)]', + 'md:py-3.5 m-0 w-full resize-none py-[13px] placeholder-black/60 bg-transparent dark:placeholder-white/60 [&:has(textarea:focus)]:shadow-[0_2px_6px_rgba(0,0,0,.05)]', isCollapsed ? 'max-h-[52px]' : 'max-h-[45vh] md:max-h-[55vh]', isMoreThanThreeRows ? 'pl-5' : 'px-5', ), @@ -222,7 +222,6 @@ const ChatForm = memo(({ index = 0 }: { index?: number }) => {
{showPlusPopover && !isAssistantsEndpoint(endpoint) && ( { )} {showMentionPopover && ( { } isSubmitting={isSubmitting} conversationId={conversationId} + specName={conversation?.spec} onChange={setBadges} isInChat={ Array.isArray(conversation?.messages) && conversation.messages.length >= 1 diff --git a/client/src/components/Chat/Input/Files/AttachFileChat.tsx b/client/src/components/Chat/Input/Files/AttachFileChat.tsx index 37b3584d3e..2f954d01d5 100644 --- a/client/src/components/Chat/Input/Files/AttachFileChat.tsx +++ b/client/src/components/Chat/Input/Files/AttachFileChat.tsx @@ -2,10 +2,9 @@ import { memo, useMemo } from 'react'; import { Constants, supportsFiles, - EModelEndpoint, mergeFileConfig, isAgentsEndpoint, - getEndpointField, + resolveEndpointType, isAssistantsEndpoint, getEndpointFileConfig, } from 'librechat-data-provider'; @@ -55,21 +54,31 @@ function AttachFileChat({ const { data: endpointsConfig } = useGetEndpointsQuery(); - const endpointType = useMemo(() => { - return ( - getEndpointField(endpointsConfig, endpoint, 'type') || - (endpoint as EModelEndpoint | undefined) - ); - }, [endpoint, endpointsConfig]); + const agentProvider = useMemo(() => { + if (!isAgents || !conversation?.agent_id) { + return undefined; + } + const agent = agentData || agentsMap?.[conversation.agent_id]; + return agent?.provider; + }, [isAgents, conversation?.agent_id, agentData, agentsMap]); + const endpointType = useMemo( + () => resolveEndpointType(endpointsConfig, endpoint, agentProvider), + [endpointsConfig, endpoint, agentProvider], + ); + + const fileConfigEndpoint = useMemo( + () => (isAgents && agentProvider ? agentProvider : endpoint), + [isAgents, agentProvider, endpoint], + ); const endpointFileConfig = useMemo( () => getEndpointFileConfig({ - endpoint, fileConfig, endpointType, + endpoint: fileConfigEndpoint, }), - [endpoint, fileConfig, endpointType], + [fileConfigEndpoint, fileConfig, endpointType], ); const endpointSupportsFiles: boolean = useMemo( () => supportsFiles[endpointType ?? endpoint ?? ''] ?? false, @@ -82,7 +91,7 @@ function AttachFileChat({ if (isAssistants && endpointSupportsFiles && !isUploadDisabled) { return ; - } else if (isAgents || (endpointSupportsFiles && !isUploadDisabled)) { + } else if ((isAgents || endpointSupportsFiles) && !isUploadDisabled) { return ( = 'image_document'; if (currentProvider === Providers.GOOGLE || currentProvider === Providers.OPENROUTER) { fileType = 'image_document_video_audio'; + } else if ( + currentProvider === Providers.BEDROCK || + endpointType === EModelEndpoint.bedrock + ) { + fileType = 'image_document_extended'; } onAction(fileType); }, diff --git a/client/src/components/Chat/Input/Files/DragDropModal.tsx b/client/src/components/Chat/Input/Files/DragDropModal.tsx index a59a7e3e9d..cb5109c866 100644 --- a/client/src/components/Chat/Input/Files/DragDropModal.tsx +++ b/client/src/components/Chat/Input/Files/DragDropModal.tsx @@ -1,14 +1,6 @@ import React, { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { OGDialog, OGDialogTemplate } from '@librechat/client'; -import { - Providers, - inferMimeType, - EToolResources, - EModelEndpoint, - defaultAgentCapabilities, - isDocumentSupportedProvider, -} from 'librechat-data-provider'; import { ImageUpIcon, FileSearch, @@ -16,6 +8,15 @@ import { FileImageIcon, TerminalSquareIcon, } from 'lucide-react'; +import { + Providers, + inferMimeType, + EToolResources, + EModelEndpoint, + isBedrockDocumentType, + defaultAgentCapabilities, + isDocumentSupportedProvider, +} from 'librechat-data-provider'; import { useAgentToolPermissions, useAgentCapabilities, @@ -77,20 +78,26 @@ const DragDropModal = ({ onOptionSelect, setShowModal, files, isVisible }: DragD ) { const supportsImageDocVideoAudio = currentProvider === EModelEndpoint.google || currentProvider === Providers.OPENROUTER; - const validFileTypes = supportsImageDocVideoAudio - ? files.every((file) => { - const type = getFileType(file); - return ( - type?.startsWith('image/') || - type?.startsWith('video/') || - type?.startsWith('audio/') || - type === 'application/pdf' - ); - }) - : files.every((file) => { - const type = getFileType(file); - return type?.startsWith('image/') || type === 'application/pdf'; - }); + const isBedrock = + currentProvider === Providers.BEDROCK || endpointType === EModelEndpoint.bedrock; + + const isValidProviderFile = (file: File): boolean => { + const type = getFileType(file); + if (supportsImageDocVideoAudio) { + return ( + type?.startsWith('image/') || + type?.startsWith('video/') || + type?.startsWith('audio/') || + type === 'application/pdf' + ); + } + if (isBedrock) { + return type?.startsWith('image/') || isBedrockDocumentType(type); + } + return type?.startsWith('image/') || type === 'application/pdf'; + }; + + const validFileTypes = files.every(isValidProviderFile); _options.push({ label: localize('com_ui_upload_provider'), diff --git a/client/src/components/Chat/Input/Files/DragDropOverlay.tsx b/client/src/components/Chat/Input/Files/DragDropOverlay.tsx index f5f45e2b88..43700206c3 100644 --- a/client/src/components/Chat/Input/Files/DragDropOverlay.tsx +++ b/client/src/components/Chat/Input/Files/DragDropOverlay.tsx @@ -36,7 +36,7 @@ const DragDropOverlay = memo(({ isActive }: DragDropOverlayProps) => { }} > {/** Content area with subtle background */} -
+
| undefined; abortUpload?: () => void; setFiles: React.Dispatch>>; - setFilesLoading: React.Dispatch>; + setFilesLoading?: React.Dispatch>; fileFilter?: (file: ExtendedFile) => boolean; assistant_id?: string; agent_id?: string; @@ -58,6 +58,7 @@ export default function FileRow({ const { deleteFile } = useFileDeletion({ mutateAsync, agent_id, assistant_id, tool_resource }); useEffect(() => { + if (!setFilesLoading) return; if (files.length === 0) { setFilesLoading(false); return; @@ -111,13 +112,15 @@ export default function FileRow({ ) .uniqueFiles.map((file: ExtendedFile, index: number) => { const handleDelete = () => { - showToast({ - message: localize('com_ui_deleting_file'), - status: 'info', - }); if (abortUpload && file.progress < 1) { abortUpload(); } + if (file.progress >= 1) { + showToast({ + message: localize('com_ui_deleting_file'), + status: 'info', + }); + } deleteFile({ file, setFiles }); }; const isImage = file.type?.startsWith('image') ?? false; @@ -133,7 +136,7 @@ export default function FileRow({ > {isImage ? ( > = {}; +let mockAgentQueryData: Partial | undefined; + +jest.mock('~/data-provider', () => ({ + useGetEndpointsQuery: () => ({ data: mockEndpointsConfig }), + useGetFileConfig: ({ select }: { select?: (data: unknown) => unknown }) => ({ + data: select != null ? select(mockFileConfig) : mockFileConfig, + }), + useGetAgentByIdQuery: () => ({ data: mockAgentQueryData }), +})); + +jest.mock('~/Providers', () => ({ + useAgentsMapContext: () => mockAgentsMap, +})); + +/** Capture the props passed to AttachFileMenu */ +let mockAttachFileMenuProps: Record = {}; +jest.mock('../AttachFileMenu', () => { + return function MockAttachFileMenu(props: Record) { + mockAttachFileMenuProps = props; + return
; + }; +}); + +jest.mock('../AttachFile', () => { + return function MockAttachFile() { + return
; + }; +}); + +const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + +function renderComponent(conversation: Record | null, disableInputs = false) { + return render( + + + + + , + ); +} + +describe('AttachFileChat', () => { + beforeEach(() => { + mockFileConfig = defaultFileConfig; + mockAgentsMap = {}; + mockAgentQueryData = undefined; + mockAttachFileMenuProps = {}; + }); + + describe('rendering decisions', () => { + it('renders AttachFileMenu for agents endpoint', () => { + renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }); + expect(screen.getByTestId('attach-file-menu')).toBeInTheDocument(); + }); + + it('renders AttachFileMenu for custom endpoint with file support', () => { + renderComponent({ endpoint: 'Moonshot' }); + expect(screen.getByTestId('attach-file-menu')).toBeInTheDocument(); + }); + + it('renders null for null conversation', () => { + const { container } = renderComponent(null); + expect(container.innerHTML).toBe(''); + }); + }); + + describe('endpointType resolution for agents', () => { + it('passes custom endpointType when agent provider is a custom endpoint', () => { + mockAgentsMap = { + 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial, + }; + renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }); + expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.custom); + }); + + it('passes openAI endpointType when agent provider is openAI', () => { + mockAgentsMap = { + 'agent-1': { provider: EModelEndpoint.openAI, model_parameters: {} } as Partial, + }; + renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }); + expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.openAI); + }); + + it('passes agents endpointType when no agent provider', () => { + renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }); + expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.agents); + }); + + it('passes agents endpointType when no agent_id', () => { + renderComponent({ endpoint: EModelEndpoint.agents }); + expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.agents); + }); + + it('uses agentData query when agent not in agentsMap', () => { + mockAgentQueryData = { provider: 'Moonshot' } as Partial; + renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-2' }); + expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.custom); + }); + }); + + describe('endpointType resolution for non-agents', () => { + it('passes custom endpointType for a custom endpoint', () => { + renderComponent({ endpoint: 'Moonshot' }); + expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.custom); + }); + + it('passes openAI endpointType for openAI endpoint', () => { + renderComponent({ endpoint: EModelEndpoint.openAI }); + expect(mockAttachFileMenuProps.endpointType).toBe(EModelEndpoint.openAI); + }); + }); + + describe('consistency: same endpoint type for direct vs agent usage', () => { + it('resolves Moonshot the same way whether used directly or through an agent', () => { + renderComponent({ endpoint: 'Moonshot' }); + const directType = mockAttachFileMenuProps.endpointType; + + mockAgentsMap = { + 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial, + }; + renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }); + const agentType = mockAttachFileMenuProps.endpointType; + + expect(directType).toBe(agentType); + }); + }); + + describe('upload disabled rendering', () => { + it('renders null for agents endpoint when fileConfig.agents.disabled is true', () => { + mockFileConfig = mergeFileConfig({ + endpoints: { + [EModelEndpoint.agents]: { disabled: true }, + }, + }); + const { container } = renderComponent({ + endpoint: EModelEndpoint.agents, + agent_id: 'agent-1', + }); + expect(container.innerHTML).toBe(''); + }); + + it('renders null for agents endpoint when disableInputs is true', () => { + const { container } = renderComponent( + { endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }, + true, + ); + expect(container.innerHTML).toBe(''); + }); + + it('renders AttachFile for assistants endpoint when not disabled', () => { + renderComponent({ endpoint: EModelEndpoint.assistants }); + expect(screen.getByTestId('attach-file')).toBeInTheDocument(); + }); + + it('renders AttachFileMenu when provider-specific config overrides agents disabled', () => { + mockFileConfig = mergeFileConfig({ + endpoints: { + Moonshot: { disabled: false, fileLimit: 5 }, + [EModelEndpoint.agents]: { disabled: true }, + }, + }); + mockAgentsMap = { + 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial, + }; + renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }); + expect(screen.getByTestId('attach-file-menu')).toBeInTheDocument(); + }); + + it('renders null for assistants endpoint when fileConfig.assistants.disabled is true', () => { + mockFileConfig = mergeFileConfig({ + endpoints: { + [EModelEndpoint.assistants]: { disabled: true }, + }, + }); + const { container } = renderComponent({ + endpoint: EModelEndpoint.assistants, + }); + expect(container.innerHTML).toBe(''); + }); + }); + + describe('endpointFileConfig resolution', () => { + it('passes Moonshot-specific file config for agent with Moonshot provider', () => { + mockAgentsMap = { + 'agent-1': { provider: 'Moonshot', model_parameters: {} } as Partial, + }; + renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }); + const config = mockAttachFileMenuProps.endpointFileConfig as { fileLimit?: number }; + expect(config?.fileLimit).toBe(5); + }); + + it('passes agents file config when agent has no specific provider config', () => { + mockAgentsMap = { + 'agent-1': { provider: EModelEndpoint.openAI, model_parameters: {} } as Partial, + }; + renderComponent({ endpoint: EModelEndpoint.agents, agent_id: 'agent-1' }); + const config = mockAttachFileMenuProps.endpointFileConfig as { fileLimit?: number }; + expect(config?.fileLimit).toBe(10); + }); + + it('passes agents file config when no agent provider', () => { + renderComponent({ endpoint: EModelEndpoint.agents }); + const config = mockAttachFileMenuProps.endpointFileConfig as { fileLimit?: number }; + expect(config?.fileLimit).toBe(20); + }); + }); +}); diff --git a/client/src/components/Chat/Input/Files/__tests__/AttachFileMenu.spec.tsx b/client/src/components/Chat/Input/Files/__tests__/AttachFileMenu.spec.tsx index d3f0fb65bc..cf08721207 100644 --- a/client/src/components/Chat/Input/Files/__tests__/AttachFileMenu.spec.tsx +++ b/client/src/components/Chat/Input/Files/__tests__/AttachFileMenu.spec.tsx @@ -1,12 +1,10 @@ import React from 'react'; import { render, screen, fireEvent } from '@testing-library/react'; -import '@testing-library/jest-dom'; import { RecoilRoot } from 'recoil'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { EModelEndpoint } from 'librechat-data-provider'; +import { EModelEndpoint, Providers } from 'librechat-data-provider'; import AttachFileMenu from '../AttachFileMenu'; -// Mock all the hooks jest.mock('~/hooks', () => ({ useAgentToolPermissions: jest.fn(), useAgentCapabilities: jest.fn(), @@ -25,53 +23,45 @@ jest.mock('~/data-provider', () => ({ })); jest.mock('~/components/SharePoint', () => ({ - SharePointPickerDialog: jest.fn(() => null), + SharePointPickerDialog: () => null, })); jest.mock('@librechat/client', () => { - const React = jest.requireActual('react'); + // eslint-disable-next-line @typescript-eslint/no-require-imports + const R = require('react'); return { - FileUpload: React.forwardRef(({ children, handleFileChange }: any, ref: any) => ( -
- - {children} -
- )), - TooltipAnchor: ({ render }: any) => render, - DropdownPopup: ({ trigger, items, isOpen, setIsOpen }: any) => { - const handleTriggerClick = () => { - if (setIsOpen) { - setIsOpen(!isOpen); - } - }; - - return ( -
-
{trigger}
- {isOpen && ( -
- {items.map((item: any, idx: number) => ( - - ))} -
- )} -
- ); - }, - AttachmentIcon: () => 📎, - SharePointIcon: () => SP, + FileUpload: (props) => R.createElement('div', { 'data-testid': 'file-upload' }, props.children), + TooltipAnchor: (props) => props.render, + DropdownPopup: (props) => + R.createElement( + 'div', + null, + R.createElement('div', { onClick: () => props.setIsOpen(!props.isOpen) }, props.trigger), + props.isOpen && + R.createElement( + 'div', + { 'data-testid': 'dropdown-menu' }, + props.items.map((item, idx) => + R.createElement( + 'button', + { key: idx, onClick: item.onClick, 'data-testid': `menu-item-${idx}` }, + item.label, + ), + ), + ), + ), + AttachmentIcon: () => R.createElement('span', { 'data-testid': 'attachment-icon' }), + SharePointIcon: () => R.createElement('span', { 'data-testid': 'sharepoint-icon' }), }; }); -jest.mock('@ariakit/react', () => ({ - MenuButton: ({ children, onClick, disabled, ...props }: any) => ( - - ), -})); +jest.mock('@ariakit/react', () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const R = require('react'); + return { + MenuButton: (props) => R.createElement('button', props, props.children), + }; +}); const mockUseAgentToolPermissions = jest.requireMock('~/hooks').useAgentToolPermissions; const mockUseAgentCapabilities = jest.requireMock('~/hooks').useAgentCapabilities; @@ -83,558 +73,283 @@ const mockUseSharePointFileHandling = jest.requireMock( ).default; const mockUseGetStartupConfig = jest.requireMock('~/data-provider').useGetStartupConfig; -describe('AttachFileMenu', () => { - const queryClient = new QueryClient({ - defaultOptions: { - queries: { retry: false }, - }, - }); +const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }); - const mockHandleFileChange = jest.fn(); - - beforeEach(() => { - jest.clearAllMocks(); - - // Default mock implementations - mockUseLocalize.mockReturnValue((key: string) => { - const translations: Record = { - com_ui_upload_provider: 'Upload to Provider', - com_ui_upload_image_input: 'Upload Image', - com_ui_upload_ocr_text: 'Upload OCR Text', - com_ui_upload_file_search: 'Upload for File Search', - com_ui_upload_code_files: 'Upload Code Files', - com_sidepanel_attach_files: 'Attach Files', - com_files_upload_sharepoint: 'Upload from SharePoint', - }; - return translations[key] || key; - }); - - mockUseAgentCapabilities.mockReturnValue({ - contextEnabled: false, - fileSearchEnabled: false, - codeEnabled: false, - }); - - mockUseGetAgentsConfig.mockReturnValue({ - agentsConfig: { - capabilities: { - contextEnabled: false, - fileSearchEnabled: false, - codeEnabled: false, - }, - }, - }); - - mockUseFileHandling.mockReturnValue({ - handleFileChange: mockHandleFileChange, - }); - - mockUseSharePointFileHandling.mockReturnValue({ - handleSharePointFiles: jest.fn(), - isProcessing: false, - downloadProgress: 0, - }); - - mockUseGetStartupConfig.mockReturnValue({ - data: { - sharePointFilePickerEnabled: false, - }, - }); - - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: undefined, - }); - }); - - const renderAttachFileMenu = (props: any = {}) => { - return render( - - - - - , - ); +function setupMocks(overrides: { provider?: string } = {}) { + const translations: Record = { + com_ui_upload_provider: 'Upload to Provider', + com_ui_upload_image_input: 'Upload Image', + com_ui_upload_ocr_text: 'Upload as Text', + com_ui_upload_file_search: 'Upload for File Search', + com_ui_upload_code_files: 'Upload Code Files', + com_sidepanel_attach_files: 'Attach Files', + com_files_upload_sharepoint: 'Upload from SharePoint', }; - - describe('Basic Rendering', () => { - it('should render the attachment button', () => { - renderAttachFileMenu(); - const button = screen.getByRole('button', { name: /attach file options/i }); - expect(button).toBeInTheDocument(); - }); - - it('should be disabled when disabled prop is true', () => { - renderAttachFileMenu({ disabled: true }); - const button = screen.getByRole('button', { name: /attach file options/i }); - expect(button).toBeDisabled(); - }); - - it('should not be disabled when disabled prop is false', () => { - renderAttachFileMenu({ disabled: false }); - const button = screen.getByRole('button', { name: /attach file options/i }); - expect(button).not.toBeDisabled(); - }); + mockUseLocalize.mockReturnValue((key: string) => translations[key] || key); + mockUseAgentCapabilities.mockReturnValue({ + contextEnabled: false, + fileSearchEnabled: false, + codeEnabled: false, }); + mockUseGetAgentsConfig.mockReturnValue({ agentsConfig: {} }); + mockUseFileHandling.mockReturnValue({ handleFileChange: jest.fn() }); + mockUseSharePointFileHandling.mockReturnValue({ + handleSharePointFiles: jest.fn(), + isProcessing: false, + downloadProgress: 0, + }); + mockUseGetStartupConfig.mockReturnValue({ data: { sharePointFilePickerEnabled: false } }); + mockUseAgentToolPermissions.mockReturnValue({ + fileSearchAllowedByAgent: false, + codeAllowedByAgent: false, + provider: overrides.provider ?? undefined, + }); +} - describe('Provider Detection Fix - endpointType Priority', () => { - it('should prioritize endpointType over currentProvider for LiteLLM gateway', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: 'litellm', // Custom gateway name NOT in documentSupportedProviders - }); +function renderMenu(props: Record = {}) { + return render( + + + + + , + ); +} - renderAttachFileMenu({ - endpoint: 'litellm', - endpointType: EModelEndpoint.openAI, // Backend override IS in documentSupportedProviders - }); +function openMenu() { + fireEvent.click(screen.getByRole('button', { name: /attach file options/i })); +} - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); +describe('AttachFileMenu', () => { + beforeEach(jest.clearAllMocks); - // With the fix, should show "Upload to Provider" because endpointType is checked first + describe('Upload to Provider vs Upload Image', () => { + it('shows "Upload to Provider" when endpointType is custom (resolved from agent provider)', () => { + setupMocks({ provider: 'Moonshot' }); + renderMenu({ endpointType: EModelEndpoint.custom }); + openMenu(); expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); expect(screen.queryByText('Upload Image')).not.toBeInTheDocument(); }); - it('should show Upload to Provider for custom endpoints with OpenAI endpointType', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: 'my-custom-gateway', - }); - - renderAttachFileMenu({ - endpoint: 'my-custom-gateway', - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - + it('shows "Upload to Provider" when endpointType is openAI', () => { + setupMocks({ provider: EModelEndpoint.openAI }); + renderMenu({ endpointType: EModelEndpoint.openAI }); + openMenu(); expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); }); - it('should show Upload Image when neither endpointType nor provider support documents', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: 'unsupported-provider', - }); + it('shows "Upload to Provider" when endpointType is anthropic', () => { + setupMocks({ provider: EModelEndpoint.anthropic }); + renderMenu({ endpointType: EModelEndpoint.anthropic }); + openMenu(); + expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); + }); - renderAttachFileMenu({ - endpoint: 'unsupported-provider', - endpointType: 'unsupported-endpoint' as any, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); + it('shows "Upload to Provider" when endpointType is google', () => { + setupMocks({ provider: Providers.GOOGLE }); + renderMenu({ endpointType: EModelEndpoint.google }); + openMenu(); + expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); + }); + it('shows "Upload Image" when endpointType is agents (no provider resolution)', () => { + setupMocks(); + renderMenu({ endpointType: EModelEndpoint.agents }); + openMenu(); expect(screen.getByText('Upload Image')).toBeInTheDocument(); expect(screen.queryByText('Upload to Provider')).not.toBeInTheDocument(); }); - it('should fallback to currentProvider when endpointType is undefined', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: EModelEndpoint.openAI, - }); - - renderAttachFileMenu({ - endpoint: EModelEndpoint.openAI, - endpointType: undefined, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); + it('shows "Upload Image" when neither endpointType nor provider supports documents', () => { + setupMocks({ provider: 'unknown-provider' }); + renderMenu({ endpointType: 'unknown-type' }); + openMenu(); + expect(screen.getByText('Upload Image')).toBeInTheDocument(); + }); + it('shows "Upload to Provider" for azureOpenAI with useResponsesApi', () => { + setupMocks({ provider: EModelEndpoint.azureOpenAI }); + renderMenu({ endpointType: EModelEndpoint.azureOpenAI, useResponsesApi: true }); + openMenu(); expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); }); - it('should fallback to currentProvider when endpointType is null', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: EModelEndpoint.anthropic, - }); - - renderAttachFileMenu({ - endpoint: EModelEndpoint.anthropic, - endpointType: null, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - - expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); + it('shows "Upload Image" for azureOpenAI without useResponsesApi', () => { + setupMocks({ provider: EModelEndpoint.azureOpenAI }); + renderMenu({ endpointType: EModelEndpoint.azureOpenAI, useResponsesApi: false }); + openMenu(); + expect(screen.getByText('Upload Image')).toBeInTheDocument(); }); }); - describe('Supported Providers', () => { - const supportedProviders = [ - { name: 'OpenAI', endpoint: EModelEndpoint.openAI }, - { name: 'Anthropic', endpoint: EModelEndpoint.anthropic }, - { name: 'Google', endpoint: EModelEndpoint.google }, - { name: 'Custom', endpoint: EModelEndpoint.custom }, - ]; - - supportedProviders.forEach(({ name, endpoint }) => { - it(`should show Upload to Provider for ${name}`, () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: endpoint, - }); - - renderAttachFileMenu({ - endpoint, - endpointType: endpoint, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - - expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); + describe('agent provider resolution scenario', () => { + it('shows "Upload to Provider" when agents endpoint has custom endpointType from provider', () => { + setupMocks({ provider: 'Moonshot' }); + renderMenu({ + endpoint: EModelEndpoint.agents, + endpointType: EModelEndpoint.custom, }); - }); - - it('should show Upload to Provider for Azure OpenAI with useResponsesApi', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: EModelEndpoint.azureOpenAI, - }); - - renderAttachFileMenu({ - endpoint: EModelEndpoint.azureOpenAI, - endpointType: EModelEndpoint.azureOpenAI, - useResponsesApi: true, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - + openMenu(); expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); }); - it('should NOT show Upload to Provider for Azure OpenAI without useResponsesApi', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: EModelEndpoint.azureOpenAI, + it('shows "Upload Image" when agents endpoint has no resolved provider type', () => { + setupMocks(); + renderMenu({ + endpoint: EModelEndpoint.agents, + endpointType: EModelEndpoint.agents, }); - - renderAttachFileMenu({ - endpoint: EModelEndpoint.azureOpenAI, - endpointType: EModelEndpoint.azureOpenAI, - useResponsesApi: false, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - - expect(screen.queryByText('Upload to Provider')).not.toBeInTheDocument(); + openMenu(); expect(screen.getByText('Upload Image')).toBeInTheDocument(); }); }); + describe('Basic Rendering', () => { + it('renders the attachment button', () => { + setupMocks(); + renderMenu(); + expect(screen.getByRole('button', { name: /attach file options/i })).toBeInTheDocument(); + }); + + it('is disabled when disabled prop is true', () => { + setupMocks(); + renderMenu({ disabled: true }); + expect(screen.getByRole('button', { name: /attach file options/i })).toBeDisabled(); + }); + + it('is not disabled when disabled prop is false', () => { + setupMocks(); + renderMenu({ disabled: false }); + expect(screen.getByRole('button', { name: /attach file options/i })).not.toBeDisabled(); + }); + }); + describe('Agent Capabilities', () => { - it('should show OCR Text option when context is enabled', () => { + it('shows OCR Text option when context is enabled', () => { + setupMocks(); mockUseAgentCapabilities.mockReturnValue({ contextEnabled: true, fileSearchEnabled: false, codeEnabled: false, }); - - renderAttachFileMenu({ - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - - expect(screen.getByText('Upload OCR Text')).toBeInTheDocument(); + renderMenu({ endpointType: EModelEndpoint.openAI }); + openMenu(); + expect(screen.getByText('Upload as Text')).toBeInTheDocument(); }); - it('should show File Search option when enabled and allowed by agent', () => { + it('shows File Search option when enabled and allowed by agent', () => { + setupMocks(); mockUseAgentCapabilities.mockReturnValue({ contextEnabled: false, fileSearchEnabled: true, codeEnabled: false, }); - mockUseAgentToolPermissions.mockReturnValue({ fileSearchAllowedByAgent: true, codeAllowedByAgent: false, provider: undefined, }); - - renderAttachFileMenu({ - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - + renderMenu({ endpointType: EModelEndpoint.openAI }); + openMenu(); expect(screen.getByText('Upload for File Search')).toBeInTheDocument(); }); - it('should NOT show File Search when enabled but not allowed by agent', () => { + it('does NOT show File Search when enabled but not allowed by agent', () => { + setupMocks(); mockUseAgentCapabilities.mockReturnValue({ contextEnabled: false, fileSearchEnabled: true, codeEnabled: false, }); - - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: undefined, - }); - - renderAttachFileMenu({ - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - + renderMenu({ endpointType: EModelEndpoint.openAI }); + openMenu(); expect(screen.queryByText('Upload for File Search')).not.toBeInTheDocument(); }); - it('should show Code Files option when enabled and allowed by agent', () => { + it('shows Code Files option when enabled and allowed by agent', () => { + setupMocks(); mockUseAgentCapabilities.mockReturnValue({ contextEnabled: false, fileSearchEnabled: false, codeEnabled: true, }); - mockUseAgentToolPermissions.mockReturnValue({ fileSearchAllowedByAgent: false, codeAllowedByAgent: true, provider: undefined, }); - - renderAttachFileMenu({ - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - + renderMenu({ endpointType: EModelEndpoint.openAI }); + openMenu(); expect(screen.getByText('Upload Code Files')).toBeInTheDocument(); }); - it('should show all options when all capabilities are enabled', () => { + it('shows all options when all capabilities are enabled', () => { + setupMocks(); mockUseAgentCapabilities.mockReturnValue({ contextEnabled: true, fileSearchEnabled: true, codeEnabled: true, }); - mockUseAgentToolPermissions.mockReturnValue({ fileSearchAllowedByAgent: true, codeAllowedByAgent: true, provider: undefined, }); - - renderAttachFileMenu({ - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - + renderMenu({ endpointType: EModelEndpoint.openAI }); + openMenu(); expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); - expect(screen.getByText('Upload OCR Text')).toBeInTheDocument(); + expect(screen.getByText('Upload as Text')).toBeInTheDocument(); expect(screen.getByText('Upload for File Search')).toBeInTheDocument(); expect(screen.getByText('Upload Code Files')).toBeInTheDocument(); }); }); describe('SharePoint Integration', () => { - it('should show SharePoint option when enabled', () => { + it('shows SharePoint option when enabled', () => { + setupMocks(); mockUseGetStartupConfig.mockReturnValue({ - data: { - sharePointFilePickerEnabled: true, - }, + data: { sharePointFilePickerEnabled: true }, }); - - renderAttachFileMenu({ - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - + renderMenu({ endpointType: EModelEndpoint.openAI }); + openMenu(); expect(screen.getByText('Upload from SharePoint')).toBeInTheDocument(); }); - it('should NOT show SharePoint option when disabled', () => { - mockUseGetStartupConfig.mockReturnValue({ - data: { - sharePointFilePickerEnabled: false, - }, - }); - - renderAttachFileMenu({ - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - + it('does NOT show SharePoint option when disabled', () => { + setupMocks(); + renderMenu({ endpointType: EModelEndpoint.openAI }); + openMenu(); expect(screen.queryByText('Upload from SharePoint')).not.toBeInTheDocument(); }); }); describe('Edge Cases', () => { - it('should handle undefined endpoint and provider gracefully', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: undefined, - }); - - renderAttachFileMenu({ - endpoint: undefined, - endpointType: undefined, - }); - + it('handles undefined endpoint and provider gracefully', () => { + setupMocks(); + renderMenu({ endpoint: undefined, endpointType: undefined }); const button = screen.getByRole('button', { name: /attach file options/i }); expect(button).toBeInTheDocument(); fireEvent.click(button); - - // Should show Upload Image as fallback expect(screen.getByText('Upload Image')).toBeInTheDocument(); }); - it('should handle null endpoint and provider gracefully', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: null, - }); - - renderAttachFileMenu({ - endpoint: null, - endpointType: null, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - expect(button).toBeInTheDocument(); + it('handles null endpoint and provider gracefully', () => { + setupMocks(); + renderMenu({ endpoint: null, endpointType: null }); + expect(screen.getByRole('button', { name: /attach file options/i })).toBeInTheDocument(); }); - it('should handle missing agentId gracefully', () => { - renderAttachFileMenu({ - agentId: undefined, - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - expect(button).toBeInTheDocument(); + it('handles missing agentId gracefully', () => { + setupMocks(); + renderMenu({ agentId: undefined, endpointType: EModelEndpoint.openAI }); + expect(screen.getByRole('button', { name: /attach file options/i })).toBeInTheDocument(); }); - it('should handle empty string agentId', () => { - renderAttachFileMenu({ - agentId: '', - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - expect(button).toBeInTheDocument(); - }); - }); - - describe('Google Provider Special Case', () => { - it('should use image_document_video_audio file type for Google provider', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: EModelEndpoint.google, - }); - - renderAttachFileMenu({ - endpoint: EModelEndpoint.google, - endpointType: EModelEndpoint.google, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - - const uploadProviderButton = screen.getByText('Upload to Provider'); - expect(uploadProviderButton).toBeInTheDocument(); - - // Click the upload to provider option - fireEvent.click(uploadProviderButton); - - // The file input should have been clicked (indirectly tested through the implementation) - }); - - it('should use image_document file type for non-Google providers', () => { - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: EModelEndpoint.openAI, - }); - - renderAttachFileMenu({ - endpoint: EModelEndpoint.openAI, - endpointType: EModelEndpoint.openAI, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - - const uploadProviderButton = screen.getByText('Upload to Provider'); - expect(uploadProviderButton).toBeInTheDocument(); - fireEvent.click(uploadProviderButton); - - // Implementation detail - image_document type is used - }); - }); - - describe('Regression Tests', () => { - it('should not break the previous behavior for direct provider attachments', () => { - // When using a direct supported provider (not through a gateway) - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: EModelEndpoint.anthropic, - }); - - renderAttachFileMenu({ - endpoint: EModelEndpoint.anthropic, - endpointType: EModelEndpoint.anthropic, - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - - expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); - }); - - it('should maintain correct priority when both are supported', () => { - // Both endpointType and provider are supported, endpointType should be checked first - mockUseAgentToolPermissions.mockReturnValue({ - fileSearchAllowedByAgent: false, - codeAllowedByAgent: false, - provider: EModelEndpoint.google, - }); - - renderAttachFileMenu({ - endpoint: EModelEndpoint.google, - endpointType: EModelEndpoint.openAI, // Different but both supported - }); - - const button = screen.getByRole('button', { name: /attach file options/i }); - fireEvent.click(button); - - // Should still work because endpointType (openAI) is supported - expect(screen.getByText('Upload to Provider')).toBeInTheDocument(); + it('handles empty string agentId', () => { + setupMocks(); + renderMenu({ agentId: '', endpointType: EModelEndpoint.openAI }); + expect(screen.getByRole('button', { name: /attach file options/i })).toBeInTheDocument(); }); }); }); diff --git a/client/src/components/Chat/Input/Files/__tests__/FileRow.spec.tsx b/client/src/components/Chat/Input/Files/__tests__/FileRow.spec.tsx index 90c1c3a7b5..ccfa19ffc8 100644 --- a/client/src/components/Chat/Input/Files/__tests__/FileRow.spec.tsx +++ b/client/src/components/Chat/Input/Files/__tests__/FileRow.spec.tsx @@ -21,6 +21,7 @@ jest.mock('~/utils', () => ({ logger: { log: jest.fn(), }, + getCachedPreview: jest.fn(() => undefined), })); jest.mock('../Image', () => { @@ -95,7 +96,7 @@ describe('FileRow', () => { }; describe('Image URL Selection Logic', () => { - it('should use filepath instead of preview when progress is 1 (upload complete)', () => { + it('should prefer cached preview over filepath when upload is complete', () => { const file = createMockFile({ file_id: 'uploaded-file', preview: 'blob:http://localhost:3080/temp-preview', @@ -109,8 +110,7 @@ describe('FileRow', () => { renderFileRow(filesMap); const imageUrl = screen.getByTestId('image-url').textContent; - expect(imageUrl).toBe('/images/user123/uploaded-file__image.png'); - expect(imageUrl).not.toContain('blob:'); + expect(imageUrl).toBe('blob:http://localhost:3080/temp-preview'); }); it('should use preview when progress is less than 1 (uploading)', () => { @@ -147,7 +147,7 @@ describe('FileRow', () => { expect(imageUrl).toBe('/images/user123/file-without-preview__image.png'); }); - it('should use filepath when both preview and filepath exist and progress is exactly 1', () => { + it('should prefer preview over filepath when both exist and progress is 1', () => { const file = createMockFile({ file_id: 'complete-file', preview: 'blob:http://localhost:3080/old-blob', @@ -161,7 +161,7 @@ describe('FileRow', () => { renderFileRow(filesMap); const imageUrl = screen.getByTestId('image-url').textContent; - expect(imageUrl).toBe('/images/user123/complete-file__image.png'); + expect(imageUrl).toBe('blob:http://localhost:3080/old-blob'); }); }); @@ -284,7 +284,7 @@ describe('FileRow', () => { const urls = screen.getAllByTestId('image-url').map((el) => el.textContent); expect(urls).toContain('blob:http://localhost:3080/preview-1'); - expect(urls).toContain('/images/user123/file-2__image.png'); + expect(urls).toContain('blob:http://localhost:3080/preview-2'); }); it('should deduplicate files with the same file_id', () => { @@ -321,10 +321,10 @@ describe('FileRow', () => { }); }); - describe('Regression: Blob URL Bug Fix', () => { - it('should NOT use revoked blob URL after upload completes', () => { + describe('Preview Cache Integration', () => { + it('should prefer preview blob URL over filepath for zero-flicker rendering', () => { const file = createMockFile({ - file_id: 'regression-test', + file_id: 'cache-test', preview: 'blob:http://localhost:3080/d25f730c-152d-41f7-8d79-c9fa448f606b', filepath: '/images/68c98b26901ebe2d87c193a2/c0fe1b93-ba3d-456c-80be-9a492bfd9ed0__image.png', @@ -337,8 +337,24 @@ describe('FileRow', () => { renderFileRow(filesMap); const imageUrl = screen.getByTestId('image-url').textContent; + expect(imageUrl).toBe('blob:http://localhost:3080/d25f730c-152d-41f7-8d79-c9fa448f606b'); + }); - expect(imageUrl).not.toContain('blob:'); + it('should fall back to filepath when no preview exists', () => { + const file = createMockFile({ + file_id: 'no-preview', + preview: undefined, + filepath: + '/images/68c98b26901ebe2d87c193a2/c0fe1b93-ba3d-456c-80be-9a492bfd9ed0__image.png', + progress: 1, + }); + + const filesMap = new Map(); + filesMap.set(file.file_id, file); + + renderFileRow(filesMap); + + const imageUrl = screen.getByTestId('image-url').textContent; expect(imageUrl).toBe( '/images/68c98b26901ebe2d87c193a2/c0fe1b93-ba3d-456c-80be-9a492bfd9ed0__image.png', ); diff --git a/client/src/components/Chat/Input/MCPSelect.tsx b/client/src/components/Chat/Input/MCPSelect.tsx index 278e603db0..13a86c856a 100644 --- a/client/src/components/Chat/Input/MCPSelect.tsx +++ b/client/src/components/Chat/Input/MCPSelect.tsx @@ -1,4 +1,4 @@ -import React, { memo, useMemo, useCallback, useRef } from 'react'; +import React, { memo, useMemo } from 'react'; import * as Ariakit from '@ariakit/react'; import { ChevronDown } from 'lucide-react'; import { PermissionTypes, Permissions } from 'librechat-data-provider'; @@ -11,7 +11,7 @@ import { useHasAccess } from '~/hooks'; import { cn } from '~/utils'; function MCPSelectContent() { - const { conversationId, mcpServerManager } = useBadgeRowContext(); + const { conversationId, storageContextKey, mcpServerManager } = useBadgeRowContext(); const { localize, isPinned, @@ -27,24 +27,9 @@ function MCPSelectContent() { const menuStore = Ariakit.useMenuStore({ focusLoop: true }); const isOpen = menuStore.useState('open'); - const focusedElementRef = useRef(null); const selectedCount = mcpValues?.length ?? 0; - // Wrap toggleServerSelection to preserve focus after state update - const handleToggle = useCallback( - (serverName: string) => { - // Save currently focused element - focusedElementRef.current = document.activeElement as HTMLElement; - toggleServerSelection(serverName); - // Restore focus after React re-renders - requestAnimationFrame(() => { - focusedElementRef.current?.focus(); - }); - }, - [toggleServerSelection], - ); - const selectedServers = useMemo(() => { if (!mcpValues || mcpValues.length === 0) { return []; @@ -103,6 +88,8 @@ function MCPSelectContent() { ))}
{configDialogProps && ( - + )} ); diff --git a/client/src/components/Chat/Input/MCPSubMenu.tsx b/client/src/components/Chat/Input/MCPSubMenu.tsx index ca547ca1f7..f8e617cba3 100644 --- a/client/src/components/Chat/Input/MCPSubMenu.tsx +++ b/client/src/components/Chat/Input/MCPSubMenu.tsx @@ -15,7 +15,7 @@ interface MCPSubMenuProps { const MCPSubMenu = React.forwardRef( ({ placeholder, ...props }, ref) => { const localize = useLocalize(); - const { mcpServerManager } = useBadgeRowContext(); + const { storageContextKey, mcpServerManager } = useBadgeRowContext(); const { isPinned, mcpValues, @@ -35,7 +35,6 @@ const MCPSubMenu = React.forwardRef( placement: 'right', }); - // Don't render if no MCP servers are configured if (!selectableServers || selectableServers.length === 0) { return null; } @@ -106,7 +105,9 @@ const MCPSubMenu = React.forwardRef(
- {configDialogProps && } + {configDialogProps && ( + + )}
); }, diff --git a/client/src/components/Chat/Input/Mention.tsx b/client/src/components/Chat/Input/Mention.tsx index 9e56068def..34bddba519 100644 --- a/client/src/components/Chat/Input/Mention.tsx +++ b/client/src/components/Chat/Input/Mention.tsx @@ -2,11 +2,10 @@ import { useState, useRef, useEffect } from 'react'; import { useCombobox } from '@librechat/client'; import { AutoSizer, List } from 'react-virtualized'; import { EModelEndpoint } from 'librechat-data-provider'; -import type { TConversation } from 'librechat-data-provider'; import type { MentionOption, ConvoGenerator } from '~/common'; import type { SetterOrUpdater } from 'recoil'; +import { useGetConversation, useLocalize, TranslationKeys } from '~/hooks'; import useSelectMention from '~/hooks/Input/useSelectMention'; -import { useLocalize, TranslationKeys } from '~/hooks'; import { useAssistantsMapContext } from '~/Providers'; import useMentions from '~/hooks/Input/useMentions'; import { removeCharIfLast } from '~/utils'; @@ -15,7 +14,6 @@ import MentionItem from './MentionItem'; const ROW_HEIGHT = 44; export default function Mention({ - conversation, setShowMentionPopover, newConversation, textAreaRef, @@ -23,7 +21,6 @@ export default function Mention({ placeholder = 'com_ui_mention', includeAssistants = true, }: { - conversation: TConversation | null; setShowMentionPopover: SetterOrUpdater; newConversation: ConvoGenerator; textAreaRef: React.MutableRefObject; @@ -32,6 +29,7 @@ export default function Mention({ includeAssistants?: boolean; }) { const localize = useLocalize(); + const getConversation = useGetConversation(0); const assistantsMap = useAssistantsMapContext(); const { options, @@ -45,9 +43,9 @@ export default function Mention({ const { onSelectMention } = useSelectMention({ presets, modelSpecs, - conversation, assistantsMap, endpointsConfig, + getConversation, newConversation, }); diff --git a/client/src/components/Chat/Input/SendButton.tsx b/client/src/components/Chat/Input/SendButton.tsx index 14c21f0586..a07e574928 100644 --- a/client/src/components/Chat/Input/SendButton.tsx +++ b/client/src/components/Chat/Input/SendButton.tsx @@ -41,7 +41,8 @@ const SubmitButton = React.memo( const SendButton = React.memo( forwardRef((props: SendButtonProps, ref: React.ForwardedRef) => { const data = useWatch({ control: props.control }); - return ; + const content = data?.text?.trim(); + return ; }), ); diff --git a/client/src/components/Chat/Input/__tests__/MCPSelect.spec.tsx b/client/src/components/Chat/Input/__tests__/MCPSelect.spec.tsx new file mode 100644 index 0000000000..7662ee5e6e --- /dev/null +++ b/client/src/components/Chat/Input/__tests__/MCPSelect.spec.tsx @@ -0,0 +1,142 @@ +import React from 'react'; +import userEvent from '@testing-library/user-event'; +import { render, screen, within } from '@testing-library/react'; +import MCPSelect from '../MCPSelect'; + +const mockToggleServerSelection = jest.fn(); + +const defaultMcpServerManager = { + localize: (key: string) => key, + isPinned: true, + mcpValues: [] as string[], + placeholderText: 'MCP Servers', + selectableServers: [ + { serverName: 'server-a', config: { title: 'Server A' } }, + { serverName: 'server-b', config: { title: 'Server B' } }, + ], + connectionStatus: {}, + isInitializing: () => false, + getConfigDialogProps: () => null, + toggleServerSelection: mockToggleServerSelection, + getServerStatusIconProps: () => null, +}; + +let mockCanUseMcp = true; +let mockMcpServerManager = { ...defaultMcpServerManager }; + +jest.mock('~/Providers', () => ({ + useBadgeRowContext: () => ({ + conversationId: 'test-conv', + storageContextKey: undefined, + mcpServerManager: mockMcpServerManager, + }), +})); + +jest.mock('~/hooks', () => ({ + useLocalize: () => (key: string) => key, + useHasAccess: () => mockCanUseMcp, +})); + +jest.mock('@librechat/client', () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const R = require('react'); + return { + TooltipAnchor: ({ + children, + render, + }: { + children: React.ReactNode; + render: React.ReactElement; + }) => R.cloneElement(render, {}, ...(Array.isArray(children) ? children : [children])), + MCPIcon: ({ className }: { className?: string }) => R.createElement('span', { className }), + Spinner: ({ className }: { className?: string }) => R.createElement('span', { className }), + }; +}); + +jest.mock('~/components/MCP/MCPConfigDialog', () => ({ + __esModule: true, + default: () => null, +})); + +describe('MCPSelect', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockCanUseMcp = true; + mockMcpServerManager = { ...defaultMcpServerManager }; + }); + + it('renders the menu button', () => { + render(); + expect(screen.getByRole('button', { name: /MCP Servers/i })).toBeInTheDocument(); + }); + + it('opens menu on button click and shows server items', async () => { + const user = userEvent.setup(); + render(); + + await user.click(screen.getByRole('button', { name: /MCP Servers/i })); + + const menu = screen.getByRole('menu', { name: /com_ui_mcp_servers/i }); + expect(menu).toBeVisible(); + expect(within(menu).getByRole('menuitemcheckbox', { name: /Server A/i })).toBeInTheDocument(); + expect(within(menu).getByRole('menuitemcheckbox', { name: /Server B/i })).toBeInTheDocument(); + }); + + it('closes menu on Escape', async () => { + const user = userEvent.setup(); + render(); + + await user.click(screen.getByRole('button', { name: /MCP Servers/i })); + expect(screen.getByRole('menu', { name: /com_ui_mcp_servers/i })).toBeVisible(); + + await user.keyboard('{Escape}'); + expect(screen.getByRole('button', { name: /MCP Servers/i })).toHaveAttribute( + 'aria-expanded', + 'false', + ); + }); + + it('keeps menu open after toggling a server item', async () => { + const user = userEvent.setup(); + render(); + + await user.click(screen.getByRole('button', { name: /MCP Servers/i })); + await user.click(screen.getByRole('menuitemcheckbox', { name: /Server A/i })); + + expect(mockToggleServerSelection).toHaveBeenCalledWith('server-a'); + expect(screen.getByRole('menu', { name: /com_ui_mcp_servers/i })).toBeVisible(); + }); + + it('arrow-key navigation wraps from last item to first', async () => { + const user = userEvent.setup(); + render(); + + await user.click(screen.getByRole('button', { name: /MCP Servers/i })); + const items = screen.getAllByRole('menuitemcheckbox'); + expect(items).toHaveLength(2); + + await user.keyboard('{ArrowDown}'); + await user.keyboard('{ArrowDown}'); + await user.keyboard('{ArrowDown}'); + expect(items[0]).toHaveFocus(); + }); + + it('renders nothing when user lacks MCP access', () => { + mockCanUseMcp = false; + const { container } = render(); + expect(container.firstChild).toBeNull(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + it('renders nothing when selectableServers is empty', () => { + mockMcpServerManager = { ...defaultMcpServerManager, selectableServers: [] }; + const { container } = render(); + expect(container.firstChild).toBeNull(); + }); + + it('renders nothing when not pinned and no servers selected', () => { + mockMcpServerManager = { ...defaultMcpServerManager, isPinned: false, mcpValues: [] }; + const { container } = render(); + expect(container.firstChild).toBeNull(); + }); +}); diff --git a/client/src/components/Chat/Input/__tests__/MCPSubMenu.spec.tsx b/client/src/components/Chat/Input/__tests__/MCPSubMenu.spec.tsx new file mode 100644 index 0000000000..be8fb5d9c2 --- /dev/null +++ b/client/src/components/Chat/Input/__tests__/MCPSubMenu.spec.tsx @@ -0,0 +1,156 @@ +import React from 'react'; +import * as Ariakit from '@ariakit/react'; +import userEvent from '@testing-library/user-event'; +import { render, screen, within } from '@testing-library/react'; +import MCPSubMenu from '../MCPSubMenu'; + +const mockToggleServerSelection = jest.fn(); +const mockSetIsPinned = jest.fn(); + +const defaultMcpServerManager = { + isPinned: true, + mcpValues: [] as string[], + setIsPinned: mockSetIsPinned, + placeholderText: 'MCP Servers', + selectableServers: [ + { serverName: 'server-a', config: { title: 'Server A' } }, + { serverName: 'server-b', config: { title: 'Server B', description: 'Second server' } }, + ], + connectionStatus: {}, + isInitializing: () => false, + getConfigDialogProps: () => null, + toggleServerSelection: mockToggleServerSelection, + getServerStatusIconProps: () => null, +}; + +let mockMcpServerManager = { ...defaultMcpServerManager }; + +jest.mock('~/Providers', () => ({ + useBadgeRowContext: () => ({ + storageContextKey: undefined, + mcpServerManager: mockMcpServerManager, + }), +})); + +jest.mock('~/hooks', () => ({ + useLocalize: () => (key: string) => key, + useHasAccess: () => true, +})); + +jest.mock('@librechat/client', () => { + // eslint-disable-next-line @typescript-eslint/no-require-imports + const R = require('react'); + return { + MCPIcon: ({ className }: { className?: string }) => R.createElement('span', { className }), + PinIcon: ({ unpin }: { unpin?: boolean }) => + R.createElement('span', { 'data-testid': unpin ? 'unpin-icon' : 'pin-icon' }), + Spinner: ({ className }: { className?: string }) => R.createElement('span', { className }), + }; +}); + +jest.mock('~/components/MCP/MCPConfigDialog', () => ({ + __esModule: true, + default: () => null, +})); + +function ParentMenu({ children }: { children: React.ReactNode }) { + return ( + + {/* eslint-disable-next-line i18next/no-literal-string */} + Parent + {children} + + ); +} + +function renderSubMenu(props: React.ComponentProps = {}) { + return render( + + + , + ); +} + +describe('MCPSubMenu', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockMcpServerManager = { ...defaultMcpServerManager }; + }); + + it('renders nothing when selectableServers is empty', () => { + mockMcpServerManager = { ...defaultMcpServerManager, selectableServers: [] }; + renderSubMenu(); + expect(screen.queryByText('MCP Servers')).not.toBeInTheDocument(); + }); + + it('renders the submenu trigger with default placeholder', () => { + renderSubMenu(); + expect(screen.getByText('MCP Servers')).toBeInTheDocument(); + }); + + it('renders custom placeholder when provided', () => { + renderSubMenu({ placeholder: 'Custom Label' }); + expect(screen.getByText('Custom Label')).toBeInTheDocument(); + expect(screen.queryByText('MCP Servers')).not.toBeInTheDocument(); + }); + + it('opens submenu and shows real server items', async () => { + const user = userEvent.setup(); + renderSubMenu(); + + await user.click(screen.getByText('MCP Servers')); + + const menu = screen.getByRole('menu', { name: /com_ui_mcp_servers/i }); + expect(menu).toBeVisible(); + expect(within(menu).getByRole('menuitemcheckbox', { name: /Server A/i })).toBeInTheDocument(); + expect(within(menu).getByRole('menuitemcheckbox', { name: /Server B/i })).toBeInTheDocument(); + }); + + it('keeps menu open after toggling a server item', async () => { + const user = userEvent.setup(); + renderSubMenu(); + + await user.click(screen.getByText('MCP Servers')); + await user.click(screen.getByRole('menuitemcheckbox', { name: /Server A/i })); + + expect(mockToggleServerSelection).toHaveBeenCalledWith('server-a'); + expect(screen.getByRole('menu', { name: /com_ui_mcp_servers/i })).toBeVisible(); + }); + + it('calls setIsPinned with toggled value when pin button is clicked', async () => { + const user = userEvent.setup(); + mockMcpServerManager = { ...defaultMcpServerManager, isPinned: false }; + renderSubMenu(); + + await user.click(screen.getByRole('button', { name: /com_ui_pin/i })); + + expect(mockSetIsPinned).toHaveBeenCalledWith(true); + }); + + it('arrow-key navigation wraps from last item to first', async () => { + const user = userEvent.setup(); + renderSubMenu(); + + await user.click(screen.getByText('MCP Servers')); + const items = screen.getAllByRole('menuitemcheckbox'); + expect(items).toHaveLength(2); + + await user.click(items[1]); + expect(items[1]).toHaveFocus(); + + await user.keyboard('{ArrowDown}'); + expect(items[0]).toHaveFocus(); + }); + + it('pin button shows unpin label when pinned', () => { + mockMcpServerManager = { ...defaultMcpServerManager, isPinned: true }; + renderSubMenu(); + expect(screen.getByRole('button', { name: /com_ui_unpin/i })).toBeInTheDocument(); + }); + + it('pin button shows pin label when not pinned', () => { + mockMcpServerManager = { ...defaultMcpServerManager, isPinned: false }; + renderSubMenu(); + expect(screen.getByRole('button', { name: /com_ui_pin/i })).toBeInTheDocument(); + }); +}); diff --git a/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx b/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx index eac3bb200c..c6f2416d78 100644 --- a/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx +++ b/client/src/components/Chat/Menus/Endpoints/ModelSelectorChatContext.tsx @@ -1,6 +1,9 @@ -import React, { createContext, useContext, useMemo } from 'react'; +import React, { createContext, useCallback, useContext, useMemo, useRef } from 'react'; +import { useRecoilValue } from 'recoil'; import type { EModelEndpoint, TConversation } from 'librechat-data-provider'; -import { useChatContext } from '~/Providers/ChatContext'; +import type { ConvoGenerator } from '~/common'; +import { useGetConversation, useNewConvo } from '~/hooks'; +import store from '~/store'; interface ModelSelectorChatContextValue { endpoint?: EModelEndpoint | null; @@ -8,8 +11,8 @@ interface ModelSelectorChatContextValue { spec?: string | null; agent_id?: string | null; assistant_id?: string | null; - conversation: TConversation | null; - newConversation: ReturnType['newConversation']; + getConversation: () => TConversation | null; + newConversation: ConvoGenerator; } const ModelSelectorChatContext = createContext( @@ -17,20 +20,34 @@ const ModelSelectorChatContext = createContext( + (params) => newConversationRef.current(params), + [], + ); /** Context value only created when relevant conversation properties change */ const contextValue = useMemo( () => ({ - endpoint: conversation?.endpoint, - model: conversation?.model, - spec: conversation?.spec, - agent_id: conversation?.agent_id, - assistant_id: conversation?.assistant_id, - conversation, + model, + spec, + agent_id, + endpoint, + assistant_id, + getConversation, newConversation, }), - [conversation, newConversation], + [endpoint, model, spec, agent_id, assistant_id, getConversation, newConversation], ); return ( diff --git a/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx b/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx index dd08728560..5a51db6ce9 100644 --- a/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx +++ b/client/src/components/Chat/Menus/Endpoints/ModelSelectorContext.tsx @@ -58,7 +58,7 @@ export function ModelSelectorProvider({ children, startupConfig }: ModelSelector const agentsMap = useAgentsMapContext(); const assistantsMap = useAssistantsMapContext(); const { data: endpointsConfig } = useGetEndpointsQuery(); - const { endpoint, model, spec, agent_id, assistant_id, conversation, newConversation } = + const { endpoint, model, spec, agent_id, assistant_id, getConversation, newConversation } = useModelSelectorChatContext(); const localize = useLocalize(); const { announcePolite } = useLiveAnnouncer(); @@ -114,7 +114,7 @@ export function ModelSelectorProvider({ children, startupConfig }: ModelSelector const { onSelectEndpoint, onSelectSpec } = useSelectMention({ // presets, modelSpecs, - conversation, + getConversation, assistantsMap, endpointsConfig, newConversation, @@ -171,90 +171,115 @@ export function ModelSelectorProvider({ children, startupConfig }: ModelSelector }, 200), [], ); - const setEndpointSearchValue = (endpoint: string, value: string) => { + const setEndpointSearchValue = useCallback((endpoint: string, value: string) => { setEndpointSearchValues((prev) => ({ ...prev, [endpoint]: value, })); - }; + }, []); - const handleSelectSpec = (spec: t.TModelSpec) => { - let model = spec.preset.model ?? null; - onSelectSpec?.(spec); - if (isAgentsEndpoint(spec.preset.endpoint)) { - model = spec.preset.agent_id ?? ''; - } else if (isAssistantsEndpoint(spec.preset.endpoint)) { - model = spec.preset.assistant_id ?? ''; - } - setSelectedValues({ - endpoint: spec.preset.endpoint, - model, - modelSpec: spec.name, - }); - }; + const handleSelectSpec = useCallback( + (spec: t.TModelSpec) => { + let model = spec.preset.model ?? null; + onSelectSpec?.(spec); + if (isAgentsEndpoint(spec.preset.endpoint)) { + model = spec.preset.agent_id ?? ''; + } else if (isAssistantsEndpoint(spec.preset.endpoint)) { + model = spec.preset.assistant_id ?? ''; + } + setSelectedValues({ + endpoint: spec.preset.endpoint, + model, + modelSpec: spec.name, + }); + }, + [onSelectSpec], + ); - const handleSelectEndpoint = (endpoint: Endpoint) => { - if (!endpoint.hasModels) { - if (endpoint.value) { - onSelectEndpoint?.(endpoint.value); + const handleSelectEndpoint = useCallback( + (endpoint: Endpoint) => { + if (!endpoint.hasModels) { + if (endpoint.value) { + onSelectEndpoint?.(endpoint.value); + } + setSelectedValues({ + endpoint: endpoint.value, + model: '', + modelSpec: '', + }); + } + }, + [onSelectEndpoint], + ); + + const handleSelectModel = useCallback( + (endpoint: Endpoint, model: string) => { + if (isAgentsEndpoint(endpoint.value)) { + onSelectEndpoint?.(endpoint.value, { + agent_id: model, + model: agentsMap?.[model]?.model ?? '', + }); + } else if (isAssistantsEndpoint(endpoint.value)) { + onSelectEndpoint?.(endpoint.value, { + assistant_id: model, + model: assistantsMap?.[endpoint.value]?.[model]?.model ?? '', + }); + } else if (endpoint.value) { + onSelectEndpoint?.(endpoint.value, { model }); } setSelectedValues({ endpoint: endpoint.value, - model: '', + model, modelSpec: '', }); - } - }; - const handleSelectModel = (endpoint: Endpoint, model: string) => { - if (isAgentsEndpoint(endpoint.value)) { - onSelectEndpoint?.(endpoint.value, { - agent_id: model, - model: agentsMap?.[model]?.model ?? '', - }); - } else if (isAssistantsEndpoint(endpoint.value)) { - onSelectEndpoint?.(endpoint.value, { - assistant_id: model, - model: assistantsMap?.[endpoint.value]?.[model]?.model ?? '', - }); - } else if (endpoint.value) { - onSelectEndpoint?.(endpoint.value, { model }); - } - setSelectedValues({ - endpoint: endpoint.value, - model, - modelSpec: '', - }); + const modelDisplayName = getModelDisplayName(endpoint, model); + const announcement = localize('com_ui_model_selected', { 0: modelDisplayName }); + announcePolite({ message: announcement, isStatus: true }); + }, + [agentsMap, announcePolite, assistantsMap, getModelDisplayName, localize, onSelectEndpoint], + ); - const modelDisplayName = getModelDisplayName(endpoint, model); - const announcement = localize('com_ui_model_selected', { 0: modelDisplayName }); - announcePolite({ message: announcement, isStatus: true }); - }; - - const value = { - // State - searchValue, - searchResults, - selectedValues, - endpointSearchValues, - // LibreChat - agentsMap, - modelSpecs, - assistantsMap, - mappedEndpoints, - endpointsConfig, - - // Functions - handleSelectSpec, - handleSelectModel, - setSelectedValues, - handleSelectEndpoint, - setEndpointSearchValue, - endpointRequiresUserKey, - setSearchValue: setDebouncedSearchValue, - // Dialog - ...keyProps, - }; + const value = useMemo( + () => ({ + searchValue, + searchResults, + selectedValues, + endpointSearchValues, + agentsMap, + modelSpecs, + assistantsMap, + mappedEndpoints, + endpointsConfig, + handleSelectSpec, + handleSelectModel, + setSelectedValues, + handleSelectEndpoint, + setEndpointSearchValue, + endpointRequiresUserKey, + setSearchValue: setDebouncedSearchValue, + ...keyProps, + }), + [ + searchValue, + searchResults, + selectedValues, + endpointSearchValues, + agentsMap, + modelSpecs, + assistantsMap, + mappedEndpoints, + endpointsConfig, + handleSelectSpec, + handleSelectModel, + setSelectedValues, + handleSelectEndpoint, + setEndpointSearchValue, + endpointRequiresUserKey, + setDebouncedSearchValue, + keyProps, + ], + ); return {children}; } diff --git a/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx b/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx index 27c1236cb2..c8cef36010 100644 --- a/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx +++ b/client/src/components/Chat/Menus/Endpoints/components/EndpointItem.tsx @@ -80,12 +80,70 @@ const SettingsButton = ({ ); }; +/** + * Lazily-rendered content for an endpoint submenu. By extracting this into a + * separate component, the expensive model-list rendering (and per-item hooks + * such as MutationObservers in EndpointModelItem) only runs when the submenu + * is actually mounted — which Ariakit defers via `unmountOnHide`. + */ +function EndpointMenuContent({ + endpoint, + endpointIndex, +}: { + endpoint: Endpoint; + endpointIndex: number; +}) { + const localize = useLocalize(); + const { agentsMap, assistantsMap, modelSpecs, selectedValues, endpointSearchValues } = + useModelSelectorContext(); + const { modelSpec: selectedSpec } = selectedValues; + const searchValue = endpointSearchValues[endpoint.value] || ''; + + const endpointSpecs = useMemo(() => { + if (!modelSpecs || !modelSpecs.length) { + return []; + } + return modelSpecs.filter((spec: TModelSpec) => spec.group === endpoint.value); + }, [modelSpecs, endpoint.value]); + + if (isAssistantsEndpoint(endpoint.value) && endpoint.models === undefined) { + return ( +
+
+ ); + } + + const filteredModels = searchValue + ? filterModels( + endpoint, + (endpoint.models || []).map((model) => model.name), + searchValue, + agentsMap, + assistantsMap, + ) + : null; + + return ( + <> + {endpointSpecs.map((spec: TModelSpec) => ( + + ))} + {filteredModels + ? renderEndpointModels(endpoint, endpoint.models || [], filteredModels, endpointIndex) + : endpoint.models && + renderEndpointModels(endpoint, endpoint.models, undefined, endpointIndex)} + + ); +} + export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) { const localize = useLocalize(); const { - agentsMap, - assistantsMap, - modelSpecs, selectedValues, handleOpenKeyDialog, handleSelectEndpoint, @@ -93,19 +151,7 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) { setEndpointSearchValue, endpointRequiresUserKey, } = useModelSelectorContext(); - const { - model: selectedModel, - endpoint: selectedEndpoint, - modelSpec: selectedSpec, - } = selectedValues; - - // Filter modelSpecs for this endpoint (by group matching endpoint value) - const endpointSpecs = useMemo(() => { - if (!modelSpecs || !modelSpecs.length) { - return []; - } - return modelSpecs.filter((spec: TModelSpec) => spec.group === endpoint.value); - }, [modelSpecs, endpoint.value]); + const { endpoint: selectedEndpoint, modelSpec: selectedSpec } = selectedValues; const searchValue = endpointSearchValues[endpoint.value] || ''; const isUserProvided = useMemo( @@ -127,18 +173,9 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
); - const isEndpointSelected = selectedEndpoint === endpoint.value; + const isEndpointSelected = !selectedSpec && selectedEndpoint === endpoint.value; if (endpoint.hasModels) { - const filteredModels = searchValue - ? filterModels( - endpoint, - (endpoint.models || []).map((model) => model.name), - searchValue, - agentsMap, - assistantsMap, - ) - : null; const placeholder = isAgentsEndpoint(endpoint.value) || isAssistantsEndpoint(endpoint.value) ? localize('com_endpoint_search_var', { 0: endpoint.label }) @@ -147,7 +184,6 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) { setEndpointSearchValue(endpoint.value, value)} combobox={} @@ -170,39 +206,7 @@ export function EndpointItem({ endpoint, endpointIndex }: EndpointItemProps) {
} > - {isAssistantsEndpoint(endpoint.value) && endpoint.models === undefined ? ( -
-
- ) : ( - <> - {/* Render modelSpecs for this endpoint */} - {endpointSpecs.map((spec: TModelSpec) => ( - - ))} - {/* Render endpoint models */} - {filteredModels - ? renderEndpointModels( - endpoint, - endpoint.models || [], - selectedModel, - filteredModels, - endpointIndex, - ) - : endpoint.models && - renderEndpointModels( - endpoint, - endpoint.models, - selectedModel, - undefined, - endpointIndex, - )} - - )} + ); } else { diff --git a/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx b/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx index 752788d63a..7cec4744d5 100644 --- a/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx +++ b/client/src/components/Chat/Menus/Endpoints/components/EndpointModelItem.tsx @@ -11,12 +11,18 @@ import { cn } from '~/utils'; interface EndpointModelItemProps { modelId: string | null; endpoint: Endpoint; - isSelected: boolean; } -export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointModelItemProps) { +export function EndpointModelItem({ modelId, endpoint }: EndpointModelItemProps) { const localize = useLocalize(); - const { handleSelectModel } = useModelSelectorContext(); + const { handleSelectModel, selectedValues } = useModelSelectorContext(); + const { + endpoint: selectedEndpoint, + model: selectedModel, + modelSpec: selectedSpec, + } = selectedValues; + const isSelected = + !selectedSpec && selectedEndpoint === endpoint.value && selectedModel === modelId; const { isFavoriteModel, toggleFavoriteModel, isFavoriteAgent, toggleFavoriteAgent } = useFavorites(); @@ -147,7 +153,6 @@ export function EndpointModelItem({ modelId, endpoint, isSelected }: EndpointMod export function renderEndpointModels( endpoint: Endpoint | null, models: Array<{ name: string; isGlobal?: boolean }>, - selectedModel: string | null, filteredModels?: string[], endpointIndex?: number, ) { @@ -161,7 +166,6 @@ export function renderEndpointModels( key={`${endpoint.value}${indexSuffix}-${modelId}-${modelIndex}`} modelId={modelId} endpoint={endpoint} - isSelected={selectedModel === modelId} /> ), ); diff --git a/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx b/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx index 34985639c5..26831a577e 100644 --- a/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx +++ b/client/src/components/Chat/Menus/Endpoints/components/SearchResults.tsx @@ -160,7 +160,9 @@ export function SearchResults({ results, localize, searchValue }: SearchResultsP } const isModelSelected = - selectedEndpoint === endpoint.value && selectedModel === modelId; + !selectedSpec && + selectedEndpoint === endpoint.value && + selectedModel === modelId; return ( ({ + useModelSelectorContext: () => ({ + handleSelectModel: mockHandleSelectModel, + selectedValues: mockSelectedValues, + }), +})); + +jest.mock('~/components/Chat/Menus/Endpoints/CustomMenu', () => { + const React = jest.requireActual('react'); + return { + CustomMenuItem: React.forwardRef(function MockMenuItem( + { children, ...rest }: { children?: React.ReactNode }, + ref: React.Ref, + ) { + return React.createElement('div', { ref, role: 'menuitem', ...rest }, children); + }), + }; +}); + +jest.mock('~/hooks', () => ({ + useLocalize: () => (key: string) => key, + useFavorites: () => ({ + isFavoriteModel: () => false, + toggleFavoriteModel: jest.fn(), + isFavoriteAgent: () => false, + toggleFavoriteAgent: jest.fn(), + }), +})); + +const baseEndpoint: Endpoint = { + value: 'anthropic', + label: 'Anthropic', + hasModels: true, + models: [{ name: 'claude-opus-4-6' }], + icon: null, +}; + +describe('EndpointModelItem', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders checkmark when model and endpoint match with no active spec', () => { + mockSelectedValues = { endpoint: 'anthropic', model: 'claude-opus-4-6', modelSpec: '' }; + render(); + + const menuItem = screen.getByRole('menuitem'); + expect(menuItem).toHaveAttribute('aria-selected', 'true'); + }); + + it('does NOT render checkmark when a model spec is active even if endpoint and model match', () => { + mockSelectedValues = { + endpoint: 'anthropic', + model: 'claude-opus-4-6', + modelSpec: 'my-anthropic-spec', + }; + render(); + + const menuItem = screen.getByRole('menuitem'); + expect(menuItem).not.toHaveAttribute('aria-selected'); + }); + + it('does NOT render checkmark when model matches but endpoint differs', () => { + mockSelectedValues = { endpoint: 'openai', model: 'claude-opus-4-6', modelSpec: '' }; + render(); + + const menuItem = screen.getByRole('menuitem'); + expect(menuItem).not.toHaveAttribute('aria-selected'); + }); + + it('does NOT render checkmark when endpoint matches but model differs', () => { + mockSelectedValues = { endpoint: 'anthropic', model: 'claude-sonnet-4-5', modelSpec: '' }; + render(); + + const menuItem = screen.getByRole('menuitem'); + expect(menuItem).not.toHaveAttribute('aria-selected'); + }); +}); diff --git a/client/src/components/Chat/Menus/Endpoints/components/__tests__/SearchResults.test.tsx b/client/src/components/Chat/Menus/Endpoints/components/__tests__/SearchResults.test.tsx new file mode 100644 index 0000000000..8ab9235f6f --- /dev/null +++ b/client/src/components/Chat/Menus/Endpoints/components/__tests__/SearchResults.test.tsx @@ -0,0 +1,109 @@ +import { render, screen } from '@testing-library/react'; +import type { Endpoint, SelectedValues } from '~/common'; +import { SearchResults } from '../SearchResults'; + +const mockHandleSelectSpec = jest.fn(); +const mockHandleSelectModel = jest.fn(); +const mockHandleSelectEndpoint = jest.fn(); +let mockSelectedValues: SelectedValues; + +jest.mock('~/components/Chat/Menus/Endpoints/ModelSelectorContext', () => ({ + useModelSelectorContext: () => ({ + selectedValues: mockSelectedValues, + handleSelectSpec: mockHandleSelectSpec, + handleSelectModel: mockHandleSelectModel, + handleSelectEndpoint: mockHandleSelectEndpoint, + endpointsConfig: {}, + }), +})); + +jest.mock('~/components/Chat/Menus/Endpoints/CustomMenu', () => { + const React = jest.requireActual('react'); + return { + CustomMenuItem: React.forwardRef(function MockMenuItem( + { children, ...rest }: { children?: React.ReactNode }, + ref: React.Ref, + ) { + return React.createElement('div', { ref, role: 'menuitem', ...rest }, children); + }), + }; +}); + +jest.mock('../SpecIcon', () => { + const React = jest.requireActual('react'); + return { + __esModule: true, + default: () => React.createElement('span', null, 'icon'), + }; +}); + +const localize = (key: string) => key; + +const anthropicEndpoint: Endpoint = { + value: 'anthropic', + label: 'Anthropic', + hasModels: true, + models: [{ name: 'claude-opus-4-6' }, { name: 'claude-sonnet-4-5' }], + icon: null, +}; + +const noModelsEndpoint: Endpoint = { + value: 'custom', + label: 'Custom', + hasModels: false, + icon: null, +}; + +describe('SearchResults', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('marks model as selected when endpoint and model match with no active spec', () => { + mockSelectedValues = { endpoint: 'anthropic', model: 'claude-opus-4-6', modelSpec: '' }; + render( + , + ); + + const items = screen.getAllByRole('menuitem'); + const selectedItem = items.find((el) => el.getAttribute('aria-selected') === 'true'); + expect(selectedItem).toBeDefined(); + expect(selectedItem).toHaveTextContent('claude-opus-4-6'); + }); + + it('does not mark model as selected when a spec is active', () => { + mockSelectedValues = { + endpoint: 'anthropic', + model: 'claude-opus-4-6', + modelSpec: 'my-spec', + }; + render( + , + ); + + const items = screen.getAllByRole('menuitem'); + for (const item of items) { + expect(item).not.toHaveAttribute('aria-selected'); + } + }); + + it('does not mark endpoint as selected when a spec is active', () => { + mockSelectedValues = { + endpoint: 'custom', + model: '', + modelSpec: 'my-spec', + }; + render(); + + const item = screen.getByRole('menuitem'); + expect(item).not.toHaveAttribute('aria-selected'); + }); + + it('marks endpoint as selected when no spec is active and endpoint matches', () => { + mockSelectedValues = { endpoint: 'custom', model: '', modelSpec: '' }; + render(); + + const item = screen.getByRole('menuitem'); + expect(item).toHaveAttribute('aria-selected', 'true'); + }); +}); diff --git a/client/src/components/Chat/Menus/HeaderNewChat.tsx b/client/src/components/Chat/Menus/HeaderNewChat.tsx index 764397eddb..a50d42af85 100644 --- a/client/src/components/Chat/Menus/HeaderNewChat.tsx +++ b/client/src/components/Chat/Menus/HeaderNewChat.tsx @@ -1,14 +1,16 @@ import { QueryKeys } from 'librechat-data-provider'; +import { useRecoilValue } from 'recoil'; import { useQueryClient } from '@tanstack/react-query'; import { TooltipAnchor, Button, NewChatIcon } from '@librechat/client'; -import { useChatContext } from '~/Providers'; +import { useNewConvo, useLocalize } from '~/hooks'; import { clearMessagesCache } from '~/utils'; -import { useLocalize } from '~/hooks'; +import store from '~/store'; export default function HeaderNewChat() { const localize = useLocalize(); const queryClient = useQueryClient(); - const { conversation, newConversation } = useChatContext(); + const { newConversation } = useNewConvo(); + const conversation = useRecoilValue(store.conversationByIndex(0)); const clickHandler: React.MouseEventHandler = (e) => { if (e.button === 0 && (e.ctrlKey || e.metaKey)) { diff --git a/client/src/components/Chat/Menus/PresetsMenu.tsx b/client/src/components/Chat/Menus/PresetsMenu.tsx index 48bd927845..a53447e014 100644 --- a/client/src/components/Chat/Menus/PresetsMenu.tsx +++ b/client/src/components/Chat/Menus/PresetsMenu.tsx @@ -1,4 +1,5 @@ import { useRef } from 'react'; +import { useRecoilValue } from 'recoil'; import { Trans } from 'react-i18next'; import { BookCopy } from 'lucide-react'; import { Content, Portal, Root, Trigger } from '@radix-ui/react-popover'; @@ -13,7 +14,7 @@ import { import type { FC } from 'react'; import { EditPresetDialog, PresetItems } from './Presets'; import { useLocalize, usePresets } from '~/hooks'; -import { useChatContext } from '~/Providers'; +import store from '~/store'; const PresetsMenu: FC = () => { const localize = useLocalize(); @@ -33,7 +34,7 @@ const PresetsMenu: FC = () => { presetToDelete, confirmDeletePreset, } = usePresets(); - const { preset } = useChatContext(); + const preset = useRecoilValue(store.presetByIndex(0)); const handleDeleteDialogChange = (open: boolean) => { setShowDeleteDialog(open); diff --git a/client/src/components/Chat/Messages/Content/ContentParts.tsx b/client/src/components/Chat/Messages/Content/ContentParts.tsx index 42ce8b8f14..4b431d7a98 100644 --- a/client/src/components/Chat/Messages/Content/ContentParts.tsx +++ b/client/src/components/Chat/Messages/Content/ContentParts.tsx @@ -15,6 +15,61 @@ import Sources from '~/components/Web/Sources'; import Container from './Container'; import Part from './Part'; +type PartWithContextProps = { + part: TMessageContentParts; + idx: number; + isLastPart: boolean; + messageId: string; + conversationId?: string | null; + nextType?: string; + isSubmitting: boolean; + isLatestMessage?: boolean; + isCreatedByUser: boolean; + isLast: boolean; + partAttachments: TAttachment[] | undefined; +}; + +const PartWithContext = memo(function PartWithContext({ + part, + idx, + isLastPart, + messageId, + conversationId, + nextType, + isSubmitting, + isLatestMessage, + isCreatedByUser, + isLast, + partAttachments, +}: PartWithContextProps) { + const contextValue = useMemo( + () => ({ + messageId, + isExpanded: true as const, + conversationId, + partIndex: idx, + nextType, + isSubmitting, + isLatestMessage, + }), + [messageId, conversationId, idx, nextType, isSubmitting, isLatestMessage], + ); + + return ( + + + + ); +}); + type ContentPartsProps = { content: Array | undefined; messageId: string; @@ -58,37 +113,24 @@ const ContentParts = memo(function ContentParts({ const attachmentMap = useMemo(() => mapAttachments(attachments ?? []), [attachments]); const effectiveIsSubmitting = isLatestMessage ? isSubmitting : false; - /** - * Render a single content part with proper context. - */ const renderPart = useCallback( (part: TMessageContentParts, idx: number, isLastPart: boolean) => { const toolCallId = (part?.[ContentTypes.TOOL_CALL] as Agents.ToolCall | undefined)?.id ?? ''; - const partAttachments = attachmentMap[toolCallId]; - return ( - - - + idx={idx} + part={part} + isLast={isLast} + messageId={messageId} + isLastPart={isLastPart} + conversationId={conversationId} + isLatestMessage={isLatestMessage} + isCreatedByUser={isCreatedByUser} + nextType={content?.[idx + 1]?.type} + isSubmitting={effectiveIsSubmitting} + partAttachments={attachmentMap[toolCallId]} + /> ); }, [ diff --git a/client/src/components/Chat/Messages/Content/DialogImage.tsx b/client/src/components/Chat/Messages/Content/DialogImage.tsx index cb496de646..b9cbe64555 100644 --- a/client/src/components/Chat/Messages/Content/DialogImage.tsx +++ b/client/src/components/Chat/Messages/Content/DialogImage.tsx @@ -4,6 +4,8 @@ import { Button, TooltipAnchor } from '@librechat/client'; import { X, ArrowDownToLine, PanelLeftOpen, PanelLeftClose, RotateCcw } from 'lucide-react'; import { useLocalize } from '~/hooks'; +const imageSizeCache = new Map(); + const getQualityStyles = (quality: string): string => { if (quality === 'high') { return 'bg-green-100 text-green-800'; @@ -50,18 +52,26 @@ export default function DialogImage({ const closeButtonRef = useRef(null); const getImageSize = useCallback(async (url: string) => { + const cached = imageSizeCache.get(url); + if (cached) { + return cached; + } try { const response = await fetch(url, { method: 'HEAD' }); const contentLength = response.headers.get('Content-Length'); if (contentLength) { const bytes = parseInt(contentLength, 10); - return formatFileSize(bytes); + const result = formatFileSize(bytes); + imageSizeCache.set(url, result); + return result; } const fullResponse = await fetch(url); const blob = await fullResponse.blob(); - return formatFileSize(blob.size); + const result = formatFileSize(blob.size); + imageSizeCache.set(url, result); + return result; } catch (error) { console.error('Error getting image size:', error); return null; @@ -355,6 +365,7 @@ export default function DialogImage({ ref={imageRef} src={src} alt="Image" + decoding="async" className="block max-h-[85vh] object-contain" style={{ maxWidth: getImageMaxWidth(), diff --git a/client/src/components/Chat/Messages/Content/Files.tsx b/client/src/components/Chat/Messages/Content/Files.tsx index 8997d5e822..504e48e883 100644 --- a/client/src/components/Chat/Messages/Content/Files.tsx +++ b/client/src/components/Chat/Messages/Content/Files.tsx @@ -1,6 +1,7 @@ import { useMemo, memo } from 'react'; import type { TFile, TMessage } from 'librechat-data-provider'; import FileContainer from '~/components/Chat/Input/Files/FileContainer'; +import { getCachedPreview } from '~/utils'; import Image from './Image'; const Files = ({ message }: { message?: TMessage }) => { @@ -17,21 +18,18 @@ const Files = ({ message }: { message?: TMessage }) => { {otherFiles.length > 0 && otherFiles.map((file) => )} {imageFiles.length > 0 && - imageFiles.map((file) => ( - - ))} + imageFiles.map((file) => { + const cached = file.file_id ? getCachedPreview(file.file_id) : undefined; + return ( + + ); + })} ); }; diff --git a/client/src/components/Chat/Messages/Content/Image.tsx b/client/src/components/Chat/Messages/Content/Image.tsx index cd72733298..7e3e12e65b 100644 --- a/client/src/components/Chat/Messages/Content/Image.tsx +++ b/client/src/components/Chat/Messages/Content/Image.tsx @@ -1,27 +1,39 @@ -import React, { useState, useRef, useMemo } from 'react'; +import React, { useState, useRef, useMemo, useEffect } from 'react'; import { Skeleton } from '@librechat/client'; -import { LazyLoadImage } from 'react-lazy-load-image-component'; import { apiBaseUrl } from 'librechat-data-provider'; -import { cn, scaleImage } from '~/utils'; import DialogImage from './DialogImage'; +import { cn } from '~/utils'; + +/** Max display height for chat images (Tailwind JIT class) */ +export const IMAGE_MAX_H = 'max-h-[45vh]' as const; +/** Matches the `max-w-lg` Tailwind class on the wrapper button (32rem = 512px at 16px base) */ +const IMAGE_MAX_W_PX = 512; + +/** Caches image dimensions by src so remounts can reserve space */ +const dimensionCache = new Map(); +/** Tracks URLs that have been fully painted — skip skeleton on remount */ +const paintedUrls = new Set(); + +/** Test-only: resets module-level caches */ +export function _resetImageCaches(): void { + dimensionCache.clear(); + paintedUrls.clear(); +} + +function computeHeightStyle(w: number, h: number): React.CSSProperties { + return { height: `min(45vh, ${(h / w) * 100}vw, ${(h / w) * IMAGE_MAX_W_PX}px)` }; +} const Image = ({ imagePath, altText, - height, - width, - placeholderDimensions, className, args, + width, + height, }: { imagePath: string; altText: string; - height: number; - width: number; - placeholderDimensions?: { - height?: string; - width?: string; - }; className?: string; args?: { prompt?: string; @@ -30,19 +42,15 @@ const Image = ({ style?: string; [key: string]: unknown; }; + width?: number; + height?: number; }) => { const [isOpen, setIsOpen] = useState(false); - const [isLoaded, setIsLoaded] = useState(false); - const containerRef = useRef(null); const triggerRef = useRef(null); - const handleImageLoad = () => setIsLoaded(true); - - // Fix image path to include base path for subdirectory deployments const absoluteImageUrl = useMemo(() => { if (!imagePath) return imagePath; - // If it's already an absolute URL or doesn't start with /images/, return as is if ( imagePath.startsWith('http') || imagePath.startsWith('data:') || @@ -51,21 +59,10 @@ const Image = ({ return imagePath; } - // Get the base URL and prepend it to the image path const baseURL = apiBaseUrl(); return `${baseURL}${imagePath}`; }, [imagePath]); - const { width: scaledWidth, height: scaledHeight } = useMemo( - () => - scaleImage({ - originalWidth: Number(placeholderDimensions?.width?.split('px')[0] ?? width), - originalHeight: Number(placeholderDimensions?.height?.split('px')[0] ?? height), - containerRef, - }), - [placeholderDimensions, height, width], - ); - const downloadImage = async () => { try { const response = await fetch(absoluteImageUrl); @@ -95,8 +92,19 @@ const Image = ({ } }; + useEffect(() => { + if (width && height && absoluteImageUrl) { + dimensionCache.set(absoluteImageUrl, { width, height }); + } + }, [absoluteImageUrl, width, height]); + + const dims = width && height ? { width, height } : dimensionCache.get(absoluteImageUrl); + const hasDimensions = !!(dims?.width && dims?.height); + const heightStyle = hasDimensions ? computeHeightStyle(dims.width, dims.height) : undefined; + const showSkeleton = hasDimensions && !paintedUrls.has(absoluteImageUrl); + return ( -
+
- {isLoaded && ( - - )} +
); }; diff --git a/client/src/components/Chat/Messages/Content/Markdown.tsx b/client/src/components/Chat/Messages/Content/Markdown.tsx index a763885d2f..1217869a2c 100644 --- a/client/src/components/Chat/Messages/Content/Markdown.tsx +++ b/client/src/components/Chat/Messages/Content/Markdown.tsx @@ -27,7 +27,7 @@ type TContentProps = { isLatestMessage: boolean; }; -const Markdown = memo(({ content = '', isLatestMessage }: TContentProps) => { +const Markdown = memo(function Markdown({ content = '', isLatestMessage }: TContentProps) { const LaTeXParsing = useRecoilValue(store.LaTeXParsing); const isInitializing = content === ''; @@ -106,5 +106,6 @@ const Markdown = memo(({ content = '', isLatestMessage }: TContentProps) => { ); }); +Markdown.displayName = 'Markdown'; export default Markdown; diff --git a/client/src/components/Chat/Messages/Content/MarkdownComponents.tsx b/client/src/components/Chat/Messages/Content/MarkdownComponents.tsx index d647147151..1c5369955d 100644 --- a/client/src/components/Chat/Messages/Content/MarkdownComponents.tsx +++ b/client/src/components/Chat/Messages/Content/MarkdownComponents.tsx @@ -18,7 +18,10 @@ type TCodeProps = { children: React.ReactNode; }; -export const code: React.ElementType = memo(({ className, children }: TCodeProps) => { +export const code: React.ElementType = memo(function MarkdownCode({ + className, + children, +}: TCodeProps) { const canRunCode = useHasAccess({ permissionType: PermissionTypes.RUN_CODE, permission: Permissions.USE, @@ -62,8 +65,12 @@ export const code: React.ElementType = memo(({ className, children }: TCodeProps ); } }); +code.displayName = 'MarkdownCode'; -export const codeNoExecution: React.ElementType = memo(({ className, children }: TCodeProps) => { +export const codeNoExecution: React.ElementType = memo(function MarkdownCodeNoExecution({ + className, + children, +}: TCodeProps) { const match = /language-(\w+)/.exec(className ?? ''); const lang = match && match[1]; @@ -82,13 +89,14 @@ export const codeNoExecution: React.ElementType = memo(({ className, children }: return ; } }); +codeNoExecution.displayName = 'MarkdownCodeNoExecution'; type TAnchorProps = { href: string; children: React.ReactNode; }; -export const a: React.ElementType = memo(({ href, children }: TAnchorProps) => { +export const a: React.ElementType = memo(function MarkdownAnchor({ href, children }: TAnchorProps) { const user = useRecoilValue(store.user); const { showToast } = useToastContext(); const localize = useLocalize(); @@ -163,14 +171,16 @@ export const a: React.ElementType = memo(({ href, children }: TAnchorProps) => { ); }); +a.displayName = 'MarkdownAnchor'; type TParagraphProps = { children: React.ReactNode; }; -export const p: React.ElementType = memo(({ children }: TParagraphProps) => { +export const p: React.ElementType = memo(function MarkdownParagraph({ children }: TParagraphProps) { return

{children}

; }); +p.displayName = 'MarkdownParagraph'; type TImageProps = { src?: string; @@ -180,7 +190,13 @@ type TImageProps = { style?: React.CSSProperties; }; -export const img: React.ElementType = memo(({ src, alt, title, className, style }: TImageProps) => { +export const img: React.ElementType = memo(function MarkdownImage({ + src, + alt, + title, + className, + style, +}: TImageProps) { // Get the base URL from the API endpoints const baseURL = apiBaseUrl(); @@ -199,3 +215,4 @@ export const img: React.ElementType = memo(({ src, alt, title, className, style return {alt}; }); +img.displayName = 'MarkdownImage'; diff --git a/client/src/components/Chat/Messages/Content/MessageContent.tsx b/client/src/components/Chat/Messages/Content/MessageContent.tsx index 7a823a07e9..0e2e7faa2c 100644 --- a/client/src/components/Chat/Messages/Content/MessageContent.tsx +++ b/client/src/components/Chat/Messages/Content/MessageContent.tsx @@ -185,4 +185,7 @@ const MessageContent = ({ ); }; -export default memo(MessageContent); +const MemoizedMessageContent = memo(MessageContent); +MemoizedMessageContent.displayName = 'MessageContent'; + +export default MemoizedMessageContent; diff --git a/client/src/components/Chat/Messages/Content/Part.tsx b/client/src/components/Chat/Messages/Content/Part.tsx index 4a74e3606f..7bce7ac11d 100644 --- a/client/src/components/Chat/Messages/Content/Part.tsx +++ b/client/src/components/Chat/Messages/Content/Part.tsx @@ -11,6 +11,7 @@ import type { TMessageContentParts, TAttachment } from 'librechat-data-provider' import { OpenAIImageGen, EmptyText, Reasoning, ExecuteCode, AgentUpdate, Text } from './Parts'; import { ErrorMessage } from './MessageContent'; import RetrievalCall from './RetrievalCall'; +import { getCachedPreview } from '~/utils'; import AgentHandoff from './AgentHandoff'; import CodeAnalyze from './CodeAnalyze'; import Container from './Container'; @@ -28,201 +29,213 @@ type PartProps = { attachments?: TAttachment[]; }; -const Part = memo( - ({ part, isSubmitting, attachments, isLast, showCursor, isCreatedByUser }: PartProps) => { - if (!part) { +const Part = memo(function Part({ + part, + isSubmitting, + attachments, + isLast, + showCursor, + isCreatedByUser, +}: PartProps) { + if (!part) { + return null; + } + + if (part.type === ContentTypes.ERROR) { + return ( + + ); + } else if (part.type === ContentTypes.AGENT_UPDATE) { + return ( + <> + + {isLast && showCursor && ( + + + + )} + + ); + } else if (part.type === ContentTypes.TEXT) { + const text = typeof part.text === 'string' ? part.text : part.text?.value; + + if (typeof text !== 'string') { + return null; + } + if (part.tool_call_ids != null && !text) { + return null; + } + /** Handle whitespace-only text to avoid layout shift */ + if (text.length > 0 && /^\s*$/.test(text)) { + /** Show placeholder for whitespace-only last part during streaming */ + if (isLast && showCursor) { + return ( + + + + ); + } + /** Skip rendering non-last whitespace-only parts to avoid empty Container */ + if (!isLast) { + return null; + } + } + return ( + + + + ); + } else if (part.type === ContentTypes.THINK) { + const reasoning = typeof part.think === 'string' ? part.think : part.think?.value; + if (typeof reasoning !== 'string') { + return null; + } + return ; + } else if (part.type === ContentTypes.TOOL_CALL) { + const toolCall = part[ContentTypes.TOOL_CALL]; + + if (!toolCall) { return null; } - if (part.type === ContentTypes.ERROR) { + const isToolCall = + 'args' in toolCall && (!toolCall.type || toolCall.type === ToolCallTypes.TOOL_CALL); + if ( + isToolCall && + (toolCall.name === Tools.execute_code || + toolCall.name === Constants.PROGRAMMATIC_TOOL_CALLING) + ) { return ( - ); - } else if (part.type === ContentTypes.AGENT_UPDATE) { + } else if ( + isToolCall && + (toolCall.name === 'image_gen_oai' || + toolCall.name === 'image_edit_oai' || + toolCall.name === 'gemini_image_gen') + ) { return ( - <> - - {isLast && showCursor && ( + + ); + } else if (isToolCall && toolCall.name === Tools.web_search) { + return ( + + ); + } else if (isToolCall && toolCall.name?.startsWith(Constants.LC_TRANSFER_TO_)) { + return ( + + ); + } else if (isToolCall) { + return ( + + ); + } else if (toolCall.type === ToolCallTypes.CODE_INTERPRETER) { + const code_interpreter = toolCall[ToolCallTypes.CODE_INTERPRETER]; + return ( + + ); + } else if ( + toolCall.type === ToolCallTypes.RETRIEVAL || + toolCall.type === ToolCallTypes.FILE_SEARCH + ) { + return ( + + ); + } else if ( + toolCall.type === ToolCallTypes.FUNCTION && + ToolCallTypes.FUNCTION in toolCall && + imageGenTools.has(toolCall.function.name) + ) { + return ( + + ); + } else if (toolCall.type === ToolCallTypes.FUNCTION && ToolCallTypes.FUNCTION in toolCall) { + if (isImageVisionTool(toolCall)) { + if (isSubmitting && showCursor) { + return ( - + - )} - - ); - } else if (part.type === ContentTypes.TEXT) { - const text = typeof part.text === 'string' ? part.text : part.text?.value; - - if (typeof text !== 'string') { - return null; - } - if (part.tool_call_ids != null && !text) { - return null; - } - /** Skip rendering if text is only whitespace to avoid empty Container */ - if (!isLast && text.length > 0 && /^\s*$/.test(text)) { - return null; - } - return ( - - - - ); - } else if (part.type === ContentTypes.THINK) { - const reasoning = typeof part.think === 'string' ? part.think : part.think?.value; - if (typeof reasoning !== 'string') { - return null; - } - return ; - } else if (part.type === ContentTypes.TOOL_CALL) { - const toolCall = part[ContentTypes.TOOL_CALL]; - - if (!toolCall) { - return null; - } - - const isToolCall = - 'args' in toolCall && (!toolCall.type || toolCall.type === ToolCallTypes.TOOL_CALL); - if ( - isToolCall && - (toolCall.name === Tools.execute_code || - toolCall.name === Constants.PROGRAMMATIC_TOOL_CALLING) - ) { - return ( - - ); - } else if ( - isToolCall && - (toolCall.name === 'image_gen_oai' || - toolCall.name === 'image_edit_oai' || - toolCall.name === 'gemini_image_gen') - ) { - return ( - - ); - } else if (isToolCall && toolCall.name === Tools.web_search) { - return ( - - ); - } else if (isToolCall && toolCall.name?.startsWith(Constants.LC_TRANSFER_TO_)) { - return ( - - ); - } else if (isToolCall) { - return ( - - ); - } else if (toolCall.type === ToolCallTypes.CODE_INTERPRETER) { - const code_interpreter = toolCall[ToolCallTypes.CODE_INTERPRETER]; - return ( - - ); - } else if ( - toolCall.type === ToolCallTypes.RETRIEVAL || - toolCall.type === ToolCallTypes.FILE_SEARCH - ) { - return ( - - ); - } else if ( - toolCall.type === ToolCallTypes.FUNCTION && - ToolCallTypes.FUNCTION in toolCall && - imageGenTools.has(toolCall.function.name) - ) { - return ( - - ); - } else if (toolCall.type === ToolCallTypes.FUNCTION && ToolCallTypes.FUNCTION in toolCall) { - if (isImageVisionTool(toolCall)) { - if (isSubmitting && showCursor) { - return ( - - - - ); - } - return null; + ); } - - return ( - - ); + return null; } - } else if (part.type === ContentTypes.IMAGE_FILE) { - const imageFile = part[ContentTypes.IMAGE_FILE]; - const height = imageFile.height ?? 1920; - const width = imageFile.width ?? 1080; + return ( - ); } + } else if (part.type === ContentTypes.IMAGE_FILE) { + const imageFile = part[ContentTypes.IMAGE_FILE]; + const cached = imageFile.file_id ? getCachedPreview(imageFile.file_id) : undefined; + return ( + + ); + } - return null; - }, -); + return null; +}); +Part.displayName = 'Part'; export default Part; diff --git a/client/src/components/Chat/Messages/Content/Parts/Attachment.tsx b/client/src/components/Chat/Messages/Content/Parts/Attachment.tsx index b5d1e07cbf..31e30772dc 100644 --- a/client/src/components/Chat/Messages/Content/Parts/Attachment.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/Attachment.tsx @@ -76,8 +76,8 @@ const ImageAttachment = memo(({ attachment }: { attachment: TAttachment }) => {
diff --git a/client/src/components/Chat/Messages/Content/Parts/EmptyText.tsx b/client/src/components/Chat/Messages/Content/Parts/EmptyText.tsx index 1b514164df..409461a058 100644 --- a/client/src/components/Chat/Messages/Content/Parts/EmptyText.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/EmptyText.tsx @@ -1,8 +1,9 @@ import { memo } from 'react'; +/** Streaming cursor placeholder — no bottom margin to match Container's structure and prevent CLS */ const EmptyTextPart = memo(() => { return ( -
+

diff --git a/client/src/components/Chat/Messages/Content/Parts/LogContent.tsx b/client/src/components/Chat/Messages/Content/Parts/LogContent.tsx index da2a8f175e..a675ff06d8 100644 --- a/client/src/components/Chat/Messages/Content/Parts/LogContent.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/LogContent.tsx @@ -12,11 +12,7 @@ interface LogContentProps { attachments?: TAttachment[]; } -type ImageAttachment = TFile & - TAttachmentMetadata & { - height: number; - width: number; - }; +type ImageAttachment = TFile & TAttachmentMetadata; const LogContent: React.FC = ({ output = '', renderImages, attachments }) => { const localize = useLocalize(); @@ -35,12 +31,8 @@ const LogContent: React.FC = ({ output = '', renderImages, atta const nonImageAtts: TAttachment[] = []; attachments?.forEach((attachment) => { - const { width, height, filepath = null } = attachment as TFile & TAttachmentMetadata; - const isImage = - imageExtRegex.test(attachment.filename ?? '') && - width != null && - height != null && - filepath != null; + const { filepath = null } = attachment as TFile & TAttachmentMetadata; + const isImage = imageExtRegex.test(attachment.filename ?? '') && filepath != null; if (isImage) { imageAtts.push(attachment as ImageAttachment); } else { @@ -100,18 +92,15 @@ const LogContent: React.FC = ({ output = '', renderImages, atta ))}

)} - {imageAttachments?.map((attachment, index) => { - const { width, height, filepath } = attachment; - return ( - - ); - })} + {imageAttachments?.map((attachment) => ( + + ))} ); }; diff --git a/client/src/components/Chat/Messages/Content/Parts/OpenAIImageGen/OpenAIImageGen.tsx b/client/src/components/Chat/Messages/Content/Parts/OpenAIImageGen/OpenAIImageGen.tsx index aa1b07827b..bdce6d3209 100644 --- a/client/src/components/Chat/Messages/Content/Parts/OpenAIImageGen/OpenAIImageGen.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/OpenAIImageGen/OpenAIImageGen.tsx @@ -1,9 +1,12 @@ -import { useState, useEffect, useRef, useCallback } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { PixelCard } from '@librechat/client'; import type { TAttachment, TFile, TAttachmentMetadata } from 'librechat-data-provider'; import Image from '~/components/Chat/Messages/Content/Image'; import ProgressText from './ProgressText'; -import { scaleImage } from '~/utils'; +import { cn } from '~/utils'; + +const IMAGE_MAX_H = 'max-h-[45vh]' as const; +const IMAGE_FULL_H = 'h-[45vh]' as const; export default function OpenAIImageGen({ initialProgress = 0.1, @@ -28,8 +31,6 @@ export default function OpenAIImageGen({ const cancelled = (!isSubmitting && initialProgress < 1) || error === true; - let width: number | undefined; - let height: number | undefined; let quality: 'low' | 'medium' | 'high' = 'high'; // Parse args if it's a string @@ -41,62 +42,21 @@ export default function OpenAIImageGen({ parsedArgs = {}; } - try { - const argsObj = parsedArgs; - - if (argsObj && typeof argsObj.size === 'string') { - const [w, h] = argsObj.size.split('x').map((v: string) => parseInt(v, 10)); - if (!isNaN(w) && !isNaN(h)) { - width = w; - height = h; - } - } else if (argsObj && (typeof argsObj.size !== 'string' || !argsObj.size)) { - width = undefined; - height = undefined; + if (parsedArgs && typeof parsedArgs.quality === 'string') { + const q = parsedArgs.quality.toLowerCase(); + if (q === 'low' || q === 'medium' || q === 'high') { + quality = q; } - - if (argsObj && typeof argsObj.quality === 'string') { - const q = argsObj.quality.toLowerCase(); - if (q === 'low' || q === 'medium' || q === 'high') { - quality = q; - } - } - } catch (e) { - width = undefined; - height = undefined; } - // Default to 1024x1024 if width and height are still undefined after parsing args and attachment metadata const attachment = attachments?.[0]; const { - width: imageWidth, - height: imageHeight, filepath = null, filename = '', + width: imgWidth, + height: imgHeight, } = (attachment as TFile & TAttachmentMetadata) || {}; - let origWidth = width ?? imageWidth; - let origHeight = height ?? imageHeight; - - if (origWidth === undefined || origHeight === undefined) { - origWidth = 1024; - origHeight = 1024; - } - - const [dimensions, setDimensions] = useState({ width: 'auto', height: 'auto' }); - const containerRef = useRef(null); - - const updateDimensions = useCallback(() => { - if (origWidth && origHeight && containerRef.current) { - const scaled = scaleImage({ - originalWidth: origWidth, - originalHeight: origHeight, - containerRef, - }); - setDimensions(scaled); - } - }, [origWidth, origHeight]); - useEffect(() => { if (isSubmitting) { setProgress(initialProgress); @@ -156,45 +116,21 @@ export default function OpenAIImageGen({ } }, [initialProgress, cancelled]); - useEffect(() => { - updateDimensions(); - - const resizeObserver = new ResizeObserver(() => { - updateDimensions(); - }); - - if (containerRef.current) { - resizeObserver.observe(containerRef.current); - } - - return () => { - resizeObserver.disconnect(); - }; - }, [updateDimensions]); - return ( <>
-
-
- {dimensions.width !== 'auto' && progress < 1 && ( - - )} +
+
+ {progress < 1 && }
diff --git a/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx b/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx index 85d1b00b4b..44d646fcd8 100644 --- a/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx @@ -1,4 +1,4 @@ -import { memo, useMemo, useState, useCallback, useRef } from 'react'; +import { memo, useMemo, useState, useCallback, useRef, useId } from 'react'; import { useAtom } from 'jotai'; import type { MouseEvent, FocusEvent } from 'react'; import { ContentTypes } from 'librechat-data-provider'; @@ -36,6 +36,7 @@ type ReasoningProps = { * For legacy text-based messages, see Thinking.tsx component. */ const Reasoning = memo(({ reasoning, isLast }: ReasoningProps) => { + const contentId = useId(); const localize = useLocalize(); const [showThinking] = useAtom(showThinkingAtom); const [isExpanded, setIsExpanded] = useState(showThinking); @@ -104,9 +105,14 @@ const Reasoning = memo(({ reasoning, isLast }: ReasoningProps) => { onClick={handleClick} label={label} content={reasoningText} + contentId={contentId} />
{ isExpanded={isExpanded} onClick={handleClick} content={reasoningText} + contentId={contentId} />
diff --git a/client/src/components/Chat/Messages/Content/Parts/Text.tsx b/client/src/components/Chat/Messages/Content/Parts/Text.tsx index c926622c9d..aec8d949e0 100644 --- a/client/src/components/Chat/Messages/Content/Parts/Text.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/Text.tsx @@ -17,7 +17,7 @@ type ContentType = | ReactElement> | ReactElement; -const TextPart = memo(({ text, isCreatedByUser, showCursor }: TextPartProps) => { +const TextPart = memo(function TextPart({ text, isCreatedByUser, showCursor }: TextPartProps) { const { isSubmitting = false, isLatestMessage = false } = useMessageContext(); const enableUserMsgMarkdown = useRecoilValue(store.enableUserMsgMarkdown); const showCursorState = useMemo(() => showCursor && isSubmitting, [showCursor, isSubmitting]); @@ -46,5 +46,6 @@ const TextPart = memo(({ text, isCreatedByUser, showCursor }: TextPartProps) =>
); }); +TextPart.displayName = 'TextPart'; export default TextPart; diff --git a/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx b/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx index 0c5992f4ab..7641738c15 100644 --- a/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/Thinking.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo, memo, useCallback, useRef, type MouseEvent } from 'react'; +import { useState, useMemo, memo, useCallback, useRef, useId, type MouseEvent } from 'react'; import { useAtomValue } from 'jotai'; import { Clipboard, CheckMark, TooltipAnchor } from '@librechat/client'; import { Lightbulb, ChevronDown, ChevronUp } from 'lucide-react'; @@ -35,12 +35,14 @@ export const ThinkingButton = memo( onClick, label, content, + contentId, showCopyButton = true, }: { isExpanded: boolean; onClick: (e: MouseEvent) => void; label: string; content?: string; + contentId: string; showCopyButton?: boolean; }) => { const localize = useLocalize(); @@ -66,6 +68,7 @@ export const ThinkingButton = memo( type="button" onClick={onClick} aria-expanded={isExpanded} + aria-controls={contentId} className={cn( 'group/button flex flex-1 items-center justify-start rounded-lg leading-[18px]', fontSize, @@ -132,11 +135,13 @@ export const FloatingThinkingBar = memo( isExpanded, onClick, content, + contentId, }: { isVisible: boolean; isExpanded: boolean; onClick: (e: MouseEvent) => void; content?: string; + contentId: string; }) => { const localize = useLocalize(); const [isCopied, setIsCopied] = useState(false); @@ -176,6 +181,8 @@ export const FloatingThinkingBar = memo( tabIndex={isVisible ? 0 : -1} onClick={onClick} aria-label={collapseTooltip} + aria-expanded={isExpanded} + aria-controls={contentId} className={cn( 'flex items-center justify-center rounded-lg bg-surface-secondary p-1.5 text-text-secondary-alt shadow-sm', 'hover:bg-surface-hover hover:text-text-primary', @@ -240,6 +247,7 @@ const Thinking: React.ElementType = memo(({ children }: { children: React.ReactN const [isExpanded, setIsExpanded] = useState(showThinking); const [isBarVisible, setIsBarVisible] = useState(false); const containerRef = useRef(null); + const contentId = useId(); const handleClick = useCallback((e: MouseEvent) => { e.preventDefault(); @@ -295,9 +303,14 @@ const Thinking: React.ElementType = memo(({ children }: { children: React.ReactN onClick={handleClick} label={label} content={textContent} + contentId={contentId} />
@@ -322,4 +336,4 @@ ThinkingContent.displayName = 'ThinkingContent'; FloatingThinkingBar.displayName = 'FloatingThinkingBar'; Thinking.displayName = 'Thinking'; -export default memo(Thinking); +export default Thinking; diff --git a/client/src/components/Chat/Messages/Content/__tests__/Image.test.tsx b/client/src/components/Chat/Messages/Content/__tests__/Image.test.tsx new file mode 100644 index 0000000000..e7e0b99f1e --- /dev/null +++ b/client/src/components/Chat/Messages/Content/__tests__/Image.test.tsx @@ -0,0 +1,179 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import Image, { _resetImageCaches } from '../Image'; + +jest.mock('~/utils', () => ({ + cn: (...classes: (string | boolean | undefined | null)[]) => + classes + .flat(Infinity) + .filter((c): c is string => typeof c === 'string' && c.length > 0) + .join(' '), +})); + +jest.mock('librechat-data-provider', () => ({ + apiBaseUrl: () => '', +})); + +jest.mock('@librechat/client', () => ({ + Skeleton: ({ className, ...props }: React.HTMLAttributes) => ( +
+ ), +})); + +jest.mock('../DialogImage', () => ({ + __esModule: true, + default: ({ isOpen, src }: { isOpen: boolean; src: string }) => + isOpen ?
: null, +})); + +describe('Image', () => { + const defaultProps = { + imagePath: '/images/test.png', + altText: 'Test image', + }; + + beforeEach(() => { + _resetImageCaches(); + jest.clearAllMocks(); + }); + + describe('rendering without dimensions', () => { + it('renders with max-h-[45vh] height constraint', () => { + render(); + const img = screen.getByRole('img'); + expect(img.className).toContain('max-h-[45vh]'); + }); + + it('renders with max-w-full to prevent landscape clipping', () => { + render(); + const img = screen.getByRole('img'); + expect(img.className).toContain('max-w-full'); + }); + + it('renders with w-auto and h-auto for natural aspect ratio', () => { + render(); + const img = screen.getByRole('img'); + expect(img.className).toContain('w-auto'); + expect(img.className).toContain('h-auto'); + }); + + it('does not show skeleton without dimensions', () => { + render(); + expect(screen.queryByTestId('skeleton')).not.toBeInTheDocument(); + }); + + it('does not apply heightStyle without dimensions', () => { + render(); + const button = screen.getByRole('button'); + expect(button.style.height).toBeFalsy(); + }); + }); + + describe('rendering with dimensions', () => { + it('shows skeleton behind image', () => { + render(); + expect(screen.getByTestId('skeleton')).toBeInTheDocument(); + }); + + it('applies computed heightStyle to button', () => { + render(); + const button = screen.getByRole('button'); + expect(button.style.height).toBeTruthy(); + expect(button.style.height).toContain('min(45vh'); + }); + + it('uses size-full object-contain on image when dimensions provided', () => { + render(); + const img = screen.getByRole('img'); + expect(img.className).toContain('size-full'); + expect(img.className).toContain('object-contain'); + }); + + it('skeleton is absolute inset-0', () => { + render(); + const skeleton = screen.getByTestId('skeleton'); + expect(skeleton.className).toContain('absolute'); + expect(skeleton.className).toContain('inset-0'); + }); + + it('marks URL as painted on load and skips skeleton on rerender', () => { + const { rerender } = render(); + const img = screen.getByRole('img'); + + expect(screen.getByTestId('skeleton')).toBeInTheDocument(); + + fireEvent.load(img); + + // Rerender same component — skeleton should not show (URL painted) + rerender(); + expect(screen.queryByTestId('skeleton')).not.toBeInTheDocument(); + }); + }); + + describe('common behavior', () => { + it('applies custom className to the button wrapper', () => { + render(); + const button = screen.getByRole('button'); + expect(button.className).toContain('mb-4'); + }); + + it('sets correct alt text', () => { + render(); + const img = screen.getByRole('img'); + expect(img).toHaveAttribute('alt', 'Test image'); + }); + + it('has correct accessibility attributes on button', () => { + render(); + const button = screen.getByRole('button'); + expect(button).toHaveAttribute('aria-label', 'View Test image in dialog'); + expect(button).toHaveAttribute('aria-haspopup', 'dialog'); + }); + }); + + describe('dialog interaction', () => { + it('opens dialog on button click', () => { + render(); + expect(screen.queryByTestId('dialog-image')).not.toBeInTheDocument(); + + const button = screen.getByRole('button'); + fireEvent.click(button); + + expect(screen.getByTestId('dialog-image')).toBeInTheDocument(); + }); + + it('dialog is always mounted (not gated by load state)', () => { + render(); + // DialogImage mock returns null when isOpen=false, but the component is in the tree + // Clicking should immediately show it + fireEvent.click(screen.getByRole('button')); + expect(screen.getByTestId('dialog-image')).toBeInTheDocument(); + }); + }); + + describe('image URL resolution', () => { + it('passes /images/ paths through with base URL', () => { + render(); + const img = screen.getByRole('img'); + expect(img).toHaveAttribute('src', '/images/test.png'); + }); + + it('passes absolute http URLs through unchanged', () => { + render(); + const img = screen.getByRole('img'); + expect(img).toHaveAttribute('src', 'https://example.com/photo.jpg'); + }); + + it('passes data URIs through unchanged', () => { + render(); + const img = screen.getByRole('img'); + expect(img).toHaveAttribute('src', 'data:image/png;base64,abc'); + }); + + it('passes non-/images/ paths through unchanged', () => { + render(); + const img = screen.getByRole('img'); + expect(img).toHaveAttribute('src', '/other/path.png'); + }); + }); +}); diff --git a/client/src/components/Chat/Messages/Content/__tests__/OpenAIImageGen.test.tsx b/client/src/components/Chat/Messages/Content/__tests__/OpenAIImageGen.test.tsx new file mode 100644 index 0000000000..ef8ac2807a --- /dev/null +++ b/client/src/components/Chat/Messages/Content/__tests__/OpenAIImageGen.test.tsx @@ -0,0 +1,182 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import OpenAIImageGen from '../Parts/OpenAIImageGen/OpenAIImageGen'; + +jest.mock('~/utils', () => ({ + cn: (...classes: (string | boolean | undefined | null)[]) => + classes + .flat(Infinity) + .filter((c): c is string => typeof c === 'string' && c.length > 0) + .join(' '), +})); + +jest.mock('~/hooks', () => ({ + useLocalize: () => (key: string) => key, +})); + +jest.mock('~/components/Chat/Messages/Content/Image', () => ({ + __esModule: true, + default: ({ + altText, + imagePath, + className, + }: { + altText: string; + imagePath: string; + className?: string; + }) => ( +
+ ), +})); + +jest.mock('@librechat/client', () => ({ + PixelCard: ({ progress }: { progress: number }) => ( +
+ ), +})); + +jest.mock('../Parts/OpenAIImageGen/ProgressText', () => ({ + __esModule: true, + default: ({ progress, error }: { progress: number; error: boolean }) => ( +
+ ), +})); + +describe('OpenAIImageGen', () => { + const defaultProps = { + initialProgress: 0.1, + isSubmitting: true, + toolName: 'image_gen_oai', + args: '{"prompt":"a cat","quality":"high","size":"1024x1024"}', + output: null as string | null, + attachments: undefined, + }; + + beforeEach(() => { + jest.clearAllMocks(); + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + describe('image preloading', () => { + it('keeps Image mounted during generation (progress < 1)', () => { + render(); + expect(screen.getByTestId('image-component')).toBeInTheDocument(); + }); + + it('hides Image with invisible absolute while progress < 1', () => { + render(); + const image = screen.getByTestId('image-component'); + expect(image.className).toContain('invisible'); + expect(image.className).toContain('absolute'); + }); + + it('shows Image without hiding classes when progress >= 1', () => { + render( + , + ); + const image = screen.getByTestId('image-component'); + expect(image.className).not.toContain('invisible'); + expect(image.className).not.toContain('absolute'); + }); + }); + + describe('PixelCard visibility', () => { + it('shows PixelCard when progress < 1', () => { + render(); + expect(screen.getByTestId('pixel-card')).toBeInTheDocument(); + }); + + it('hides PixelCard when progress >= 1', () => { + render(); + expect(screen.queryByTestId('pixel-card')).not.toBeInTheDocument(); + }); + }); + + describe('layout classes', () => { + it('applies max-h-[45vh] to the outer container', () => { + const { container } = render(); + const outerDiv = container.querySelector('[class*="max-h-"]'); + expect(outerDiv?.className).toContain('max-h-[45vh]'); + }); + + it('applies h-[45vh] w-full to inner container during loading', () => { + const { container } = render(); + const innerDiv = container.querySelector('[class*="h-[45vh]"]'); + expect(innerDiv).not.toBeNull(); + expect(innerDiv?.className).toContain('w-full'); + }); + + it('applies w-auto to inner container when complete', () => { + const { container } = render( + , + ); + const overflowDiv = container.querySelector('[class*="overflow-hidden"]'); + expect(overflowDiv?.className).toContain('w-auto'); + }); + }); + + describe('args parsing', () => { + it('parses quality from args', () => { + render(); + expect(screen.getByTestId('progress-text')).toBeInTheDocument(); + }); + + it('handles invalid JSON args gracefully', () => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + render(); + expect(screen.getByTestId('image-component')).toBeInTheDocument(); + consoleSpy.mockRestore(); + }); + + it('handles object args', () => { + render( + , + ); + expect(screen.getByTestId('image-component')).toBeInTheDocument(); + }); + }); + + describe('cancellation', () => { + it('shows error state when output contains error', () => { + render( + , + ); + const progressText = screen.getByTestId('progress-text'); + expect(progressText).toHaveAttribute('data-error', 'true'); + }); + + it('shows cancelled state when not submitting and incomplete', () => { + render(); + const progressText = screen.getByTestId('progress-text'); + expect(progressText).toHaveAttribute('data-error', 'true'); + }); + }); +}); diff --git a/client/src/components/Chat/Messages/HoverButtons.tsx b/client/src/components/Chat/Messages/HoverButtons.tsx index 5d60223d08..180e8b599e 100644 --- a/client/src/components/Chat/Messages/HoverButtons.tsx +++ b/client/src/components/Chat/Messages/HoverButtons.tsx @@ -18,7 +18,7 @@ type THoverButtons = { message: TMessage; regenerate: () => void; handleContinue: (e: React.MouseEvent) => void; - latestMessage: TMessage | null; + latestMessageId?: string; isLast: boolean; index: number; handleFeedback?: ({ feedback }: { feedback: TFeedback | undefined }) => void; @@ -119,7 +119,7 @@ const HoverButtons = ({ message, regenerate, handleContinue, - latestMessage, + latestMessageId, isLast, handleFeedback, }: THoverButtons) => { @@ -143,7 +143,7 @@ const HoverButtons = ({ searchResult: message.searchResult, finish_reason: message.finish_reason, isCreatedByUser: message.isCreatedByUser, - latestMessageId: latestMessage?.messageId, + latestMessageId: latestMessageId, }); const { @@ -239,7 +239,7 @@ const HoverButtons = ({ messageId={message.messageId} conversationId={conversation.conversationId} forkingSupported={forkingSupported} - latestMessageId={latestMessage?.messageId} + latestMessageId={latestMessageId} isLast={isLast} /> diff --git a/client/src/components/Chat/Messages/Message.tsx b/client/src/components/Chat/Messages/Message.tsx index 78e08e3631..f9db38fdab 100644 --- a/client/src/components/Chat/Messages/Message.tsx +++ b/client/src/components/Chat/Messages/Message.tsx @@ -4,25 +4,23 @@ import type { TMessageProps } from '~/common'; import MessageRender from './ui/MessageRender'; import MultiMessage from './MultiMessage'; -const MessageContainer = React.memo( - ({ - handleScroll, - children, - }: { - handleScroll: (event?: unknown) => void; - children: React.ReactNode; - }) => { - return ( -
- {children} -
- ); - }, -); +const MessageContainer = React.memo(function MessageContainer({ + handleScroll, + children, +}: { + handleScroll: (event?: unknown) => void; + children: React.ReactNode; +}) { + return ( +
+ {children} +
+ ); +}); export default function Message(props: TMessageProps) { const { conversation, handleScroll } = useMessageProcess({ diff --git a/client/src/components/Chat/Messages/MessageParts.tsx b/client/src/components/Chat/Messages/MessageParts.tsx index 0005ee0499..7aa73a54e6 100644 --- a/client/src/components/Chat/Messages/MessageParts.tsx +++ b/client/src/components/Chat/Messages/MessageParts.tsx @@ -32,7 +32,7 @@ export default function Message(props: TMessageProps) { handleScroll, conversation, isSubmitting, - latestMessage, + latestMessageId, handleContinue, copyToClipboard, regenerateMessage, @@ -129,7 +129,7 @@ export default function Message(props: TMessageProps) { )}
-
+
} />
{isLast && isSubmitting ? ( -
+
) : ( regenerateMessage()} copyToClipboard={copyToClipboard} handleContinue={handleContinue} - latestMessage={latestMessage} + latestMessageId={latestMessageId} isLast={isLast} /> diff --git a/client/src/components/Chat/Messages/ui/MessageRender.tsx b/client/src/components/Chat/Messages/ui/MessageRender.tsx index 0d40b4a98f..e261a576bd 100644 --- a/client/src/components/Chat/Messages/ui/MessageRender.tsx +++ b/client/src/components/Chat/Messages/ui/MessageRender.tsx @@ -4,11 +4,11 @@ import { useRecoilValue } from 'recoil'; import { type TMessage } from 'librechat-data-provider'; import type { TMessageProps, TMessageIcon } from '~/common'; import MessageContent from '~/components/Chat/Messages/Content/MessageContent'; +import { useLocalize, useMessageActions, useContentMetadata } from '~/hooks'; import PlaceholderRow from '~/components/Chat/Messages/ui/PlaceholderRow'; import SiblingSwitch from '~/components/Chat/Messages/SiblingSwitch'; import HoverButtons from '~/components/Chat/Messages/HoverButtons'; import MessageIcon from '~/components/Chat/Messages/MessageIcon'; -import { useLocalize, useMessageActions, useContentMetadata } from '~/hooks'; import SubRow from '~/components/Chat/Messages/SubRow'; import { cn, getMessageAriaLabel } from '~/utils'; import { fontSizeAtom } from '~/store/fontSize'; @@ -23,180 +23,183 @@ type MessageRenderProps = { 'currentEditId' | 'setCurrentEditId' | 'siblingIdx' | 'setSiblingIdx' | 'siblingCount' >; -const MessageRender = memo( - ({ +const MessageRender = memo(function MessageRender({ + message: msg, + siblingIdx, + siblingCount, + setSiblingIdx, + currentEditId, + setCurrentEditId, + isSubmitting = false, +}: MessageRenderProps) { + const localize = useLocalize(); + const { + ask, + edit, + index, + agent, + assistant, + enterEdit, + conversation, + messageLabel, + handleFeedback, + handleContinue, + latestMessageId, + copyToClipboard, + regenerateMessage, + latestMessageDepth, + } = useMessageActions({ message: msg, - siblingIdx, - siblingCount, - setSiblingIdx, currentEditId, setCurrentEditId, - isSubmitting = false, - }: MessageRenderProps) => { - const localize = useLocalize(); - const { - ask, - edit, - index, - agent, - assistant, - enterEdit, - conversation, + }); + const fontSize = useAtomValue(fontSizeAtom); + const maximizeChatSpace = useRecoilValue(store.maximizeChatSpace); + + const handleRegenerateMessage = useCallback(() => regenerateMessage(), [regenerateMessage]); + const hasNoChildren = !(msg?.children?.length ?? 0); + const isLast = useMemo( + () => hasNoChildren && (msg?.depth === latestMessageDepth || msg?.depth === -1), + [hasNoChildren, msg?.depth, latestMessageDepth], + ); + const isLatestMessage = msg?.messageId === latestMessageId; + /** Only pass isSubmitting to the latest message to prevent unnecessary re-renders */ + const effectiveIsSubmitting = isLatestMessage ? isSubmitting : false; + + const iconData: TMessageIcon = useMemo( + () => ({ + endpoint: msg?.endpoint ?? conversation?.endpoint, + model: msg?.model ?? conversation?.model, + iconURL: msg?.iconURL, + modelLabel: messageLabel, + isCreatedByUser: msg?.isCreatedByUser, + }), + [ messageLabel, - latestMessage, - handleFeedback, - handleContinue, - copyToClipboard, - regenerateMessage, - } = useMessageActions({ - message: msg, - currentEditId, - setCurrentEditId, - }); - const fontSize = useAtomValue(fontSizeAtom); - const maximizeChatSpace = useRecoilValue(store.maximizeChatSpace); + conversation?.endpoint, + conversation?.model, + msg?.model, + msg?.iconURL, + msg?.endpoint, + msg?.isCreatedByUser, + ], + ); - const handleRegenerateMessage = useCallback(() => regenerateMessage(), [regenerateMessage]); - const hasNoChildren = !(msg?.children?.length ?? 0); - const isLast = useMemo( - () => hasNoChildren && (msg?.depth === latestMessage?.depth || msg?.depth === -1), - [hasNoChildren, msg?.depth, latestMessage?.depth], - ); - const isLatestMessage = msg?.messageId === latestMessage?.messageId; - /** Only pass isSubmitting to the latest message to prevent unnecessary re-renders */ - const effectiveIsSubmitting = isLatestMessage ? isSubmitting : false; + const { hasParallelContent } = useContentMetadata(msg); + const messageId = msg?.messageId ?? ''; + const messageContextValue = useMemo( + () => ({ + messageId, + isLatestMessage, + isExpanded: false as const, + isSubmitting: effectiveIsSubmitting, + conversationId: conversation?.conversationId, + }), + [messageId, conversation?.conversationId, effectiveIsSubmitting, isLatestMessage], + ); - const iconData: TMessageIcon = useMemo( - () => ({ - endpoint: msg?.endpoint ?? conversation?.endpoint, - model: msg?.model ?? conversation?.model, - iconURL: msg?.iconURL, - modelLabel: messageLabel, - isCreatedByUser: msg?.isCreatedByUser, - }), - [ - messageLabel, - conversation?.endpoint, - conversation?.model, - msg?.model, - msg?.iconURL, - msg?.endpoint, - msg?.isCreatedByUser, - ], - ); + if (!msg) { + return null; + } - const { hasParallelContent } = useContentMetadata(msg); - - if (!msg) { - return null; + const getChatWidthClass = () => { + if (maximizeChatSpace) { + return 'w-full max-w-full md:px-5 lg:px-1 xl:px-5'; } + if (hasParallelContent) { + return 'md:max-w-[58rem] xl:max-w-[70rem]'; + } + return 'md:max-w-[47rem] xl:max-w-[55rem]'; + }; - const getChatWidthClass = () => { - if (maximizeChatSpace) { - return 'w-full max-w-full md:px-5 lg:px-1 xl:px-5'; - } - if (hasParallelContent) { - return 'md:max-w-[58rem] xl:max-w-[70rem]'; - } - return 'md:max-w-[47rem] xl:max-w-[55rem]'; - }; + const baseClasses = { + common: 'group mx-auto flex flex-1 gap-3 transition-all duration-300 transform-gpu ', + chat: getChatWidthClass(), + }; - const baseClasses = { - common: 'group mx-auto flex flex-1 gap-3 transition-all duration-300 transform-gpu ', - chat: getChatWidthClass(), - }; + const conditionalClasses = { + focus: 'focus:outline-none focus:ring-2 focus:ring-border-xheavy', + }; - const conditionalClasses = { - focus: 'focus:outline-none focus:ring-2 focus:ring-border-xheavy', - }; + return ( +
+ {!hasParallelContent && ( +
+
+ +
+
+ )} - return (
{!hasParallelContent && ( -
-
- -
-
+

{messageLabel}

)} -
- {!hasParallelContent && ( -

{messageLabel}

- )} - -
-
- - ({}))} - /> - -
- {hasNoChildren && effectiveIsSubmitting ? ( - - ) : ( - - - - - )} +
+
+ + ({}))} + /> +
+ {hasNoChildren && effectiveIsSubmitting ? ( + + ) : ( + + + + + )}
- ); - }, -); +
+ ); +}); +MessageRender.displayName = 'MessageRender'; export default MessageRender; diff --git a/client/src/components/Chat/Messages/ui/PlaceholderRow.tsx b/client/src/components/Chat/Messages/ui/PlaceholderRow.tsx index d67424a46f..e60dc28278 100644 --- a/client/src/components/Chat/Messages/ui/PlaceholderRow.tsx +++ b/client/src/components/Chat/Messages/ui/PlaceholderRow.tsx @@ -1,7 +1,9 @@ import { memo } from 'react'; -const PlaceholderRow = memo(() => { - return
; +/** Height matches the SubRow action buttons row (31px) — keep in sync with HoverButtons */ +const PlaceholderRow = memo(function PlaceholderRow() { + return
; }); +PlaceholderRow.displayName = 'PlaceholderRow'; export default PlaceholderRow; diff --git a/client/src/components/Chat/TemporaryChat.tsx b/client/src/components/Chat/TemporaryChat.tsx index d09cc73289..a4d72d081e 100644 --- a/client/src/components/Chat/TemporaryChat.tsx +++ b/client/src/components/Chat/TemporaryChat.tsx @@ -1,8 +1,8 @@ import React from 'react'; +import { useRecoilValue } from 'recoil'; import { TooltipAnchor } from '@librechat/client'; import { MessageCircleDashed } from 'lucide-react'; import { useRecoilState, useRecoilCallback } from 'recoil'; -import { useChatContext } from '~/Providers'; import { useLocalize } from '~/hooks'; import { cn } from '~/utils'; import store from '~/store'; @@ -10,13 +10,8 @@ import store from '~/store'; export function TemporaryChat() { const localize = useLocalize(); const [isTemporary, setIsTemporary] = useRecoilState(store.isTemporary); - const { conversation, isSubmitting } = useChatContext(); - - const temporaryBadge = { - id: 'temporary', - atom: store.isTemporary, - isAvailable: true, - }; + const conversation = useRecoilValue(store.conversationByIndex(0)); + const isSubmitting = useRecoilValue(store.isSubmittingFamily(0)); const handleBadgeToggle = useRecoilCallback( () => () => { diff --git a/client/src/components/Conversations/Conversations.tsx b/client/src/components/Conversations/Conversations.tsx index fc66c0977a..c7eb4d53ef 100644 --- a/client/src/components/Conversations/Conversations.tsx +++ b/client/src/components/Conversations/Conversations.tsx @@ -384,6 +384,7 @@ const Conversations: FC = ({ onRowsRendered={handleRowsRendered} tabIndex={-1} style={{ outline: 'none', scrollbarGutter: 'stable' }} + containerRole="rowgroup" /> )} diff --git a/client/src/components/Endpoints/Icon.tsx b/client/src/components/Endpoints/Icon.tsx index 3256145bfb..fae0f286d3 100644 --- a/client/src/components/Endpoints/Icon.tsx +++ b/client/src/components/Endpoints/Icon.tsx @@ -1,64 +1,102 @@ -import React, { memo, useState } from 'react'; +import React, { memo } from 'react'; import { UserIcon, useAvatar } from '@librechat/client'; -import type { TUser } from 'librechat-data-provider'; import type { IconProps } from '~/common'; import MessageEndpointIcon from './MessageEndpointIcon'; import { useAuthContext } from '~/hooks/AuthContext'; import { useLocalize } from '~/hooks'; import { cn } from '~/utils'; +type ResolvedAvatar = { type: 'image'; src: string } | { type: 'fallback' }; + +/** + * Caches the resolved avatar decision per user ID. + * Invalidated when `user.avatar` changes (e.g., settings upload). + * Tracks failed image URLs so they fall back to SVG permanently for the session. + */ +const avatarCache = new Map< + string, + { avatar: string; avatarSrc: string; resolved: ResolvedAvatar } +>(); +const failedUrls = new Set(); + +function resolveAvatar(userId: string, userAvatar: string, avatarSrc: string): ResolvedAvatar { + if (!userId) { + const imgSrc = userAvatar || avatarSrc; + return imgSrc && !failedUrls.has(imgSrc) + ? { type: 'image', src: imgSrc } + : { type: 'fallback' }; + } + + const cached = avatarCache.get(userId); + if (cached && cached.avatar === userAvatar && cached.avatarSrc === avatarSrc) { + return cached.resolved; + } + + const imgSrc = userAvatar || avatarSrc; + const resolved: ResolvedAvatar = + imgSrc && !failedUrls.has(imgSrc) ? { type: 'image', src: imgSrc } : { type: 'fallback' }; + + avatarCache.set(userId, { avatar: userAvatar, avatarSrc, resolved }); + return resolved; +} + +function markAvatarFailed(userId: string, src: string): ResolvedAvatar { + failedUrls.add(src); + const fallback: ResolvedAvatar = { type: 'fallback' }; + const cached = avatarCache.get(userId); + if (cached) { + avatarCache.set(userId, { ...cached, resolved: fallback }); + } + return fallback; +} + type UserAvatarProps = { size: number; - user?: TUser; + avatar: string; avatarSrc: string; + userId: string; username: string; className?: string; }; -const UserAvatar = memo(({ size, user, avatarSrc, username, className }: UserAvatarProps) => { - const [imageError, setImageError] = useState(false); +const UserAvatar = memo( + ({ size, avatar, avatarSrc, userId, username, className }: UserAvatarProps) => { + const [resolved, setResolved] = React.useState(() => resolveAvatar(userId, avatar, avatarSrc)); - const handleImageError = () => { - setImageError(true); - }; + React.useEffect(() => { + setResolved(resolveAvatar(userId, avatar, avatarSrc)); + }, [userId, avatar, avatarSrc]); - const renderDefaultAvatar = () => ( -
- -
- ); - - return ( -
- {(!(user?.avatar ?? '') && (!(user?.username ?? '') || user?.username.trim() === '')) || - imageError ? ( - renderDefaultAvatar() - ) : ( - avatar - )} -
- ); -}); + return ( +
+ {resolved.type === 'image' ? ( + avatar setResolved(markAvatarFailed(userId, resolved.src))} + /> + ) : ( +
+ +
+ )} +
+ ); + }, +); UserAvatar.displayName = 'UserAvatar'; @@ -74,9 +112,10 @@ const Icon: React.FC = memo((props) => { return ( ); diff --git a/client/src/components/MCP/MCPConfigDialog.tsx b/client/src/components/MCP/MCPConfigDialog.tsx index a3727971e9..f1079c2799 100644 --- a/client/src/components/MCP/MCPConfigDialog.tsx +++ b/client/src/components/MCP/MCPConfigDialog.tsx @@ -24,6 +24,7 @@ interface MCPConfigDialogProps { serverName: string; serverStatus?: MCPServerStatus; conversationId?: string | null; + storageContextKey?: string; } export default function MCPConfigDialog({ @@ -36,6 +37,7 @@ export default function MCPConfigDialog({ serverName, serverStatus, conversationId, + storageContextKey, }: MCPConfigDialogProps) { const localize = useLocalize(); @@ -167,6 +169,7 @@ export default function MCPConfigDialog({ 0} /> diff --git a/client/src/components/MCP/MCPServerMenuItem.tsx b/client/src/components/MCP/MCPServerMenuItem.tsx index 2291a5233e..7fcb773bb9 100644 --- a/client/src/components/MCP/MCPServerMenuItem.tsx +++ b/client/src/components/MCP/MCPServerMenuItem.tsx @@ -46,7 +46,6 @@ export default function MCPServerMenuItem({ name="mcp-servers" value={server.serverName} checked={isSelected} - setValueOnChange={false} onChange={() => onToggle(server.serverName)} aria-label={accessibleLabel} className={cn( diff --git a/client/src/components/MCP/ServerInitializationSection.tsx b/client/src/components/MCP/ServerInitializationSection.tsx index b5f71335d7..c080866b3d 100644 --- a/client/src/components/MCP/ServerInitializationSection.tsx +++ b/client/src/components/MCP/ServerInitializationSection.tsx @@ -9,12 +9,14 @@ interface ServerInitializationSectionProps { requiresOAuth: boolean; hasCustomUserVars?: boolean; conversationId?: string | null; + storageContextKey?: string; } export default function ServerInitializationSection({ serverName, requiresOAuth, conversationId, + storageContextKey, sidePanel = false, hasCustomUserVars = false, }: ServerInitializationSectionProps) { @@ -28,7 +30,7 @@ export default function ServerInitializationSection({ initializeServer, availableMCPServers, revokeOAuthForServer, - } = useMCPServerManager({ conversationId }); + } = useMCPServerManager({ conversationId, storageContextKey }); const { connectionStatus } = useMCPConnectionStatus({ enabled: !!availableMCPServers && availableMCPServers.length > 0, diff --git a/client/src/components/Messages/Content/CodeBlock.tsx b/client/src/components/Messages/Content/CodeBlock.tsx index eae84e49a9..7407098c5e 100644 --- a/client/src/components/Messages/Content/CodeBlock.tsx +++ b/client/src/components/Messages/Content/CodeBlock.tsx @@ -23,125 +23,138 @@ interface FloatingCodeBarProps extends CodeBarProps { isVisible: boolean; } -const CodeBar: React.FC = React.memo( - ({ lang, error, codeRef, blockIndex, plugin = null, allowExecution = true }) => { - const localize = useLocalize(); - const [isCopied, setIsCopied] = useState(false); - return ( -
- {lang} - {plugin === true ? ( - - ) : ( -
- {allowExecution === true && ( - +const CodeBar: React.FC = React.memo(function CodeBar({ + lang, + error, + codeRef, + blockIndex, + plugin = null, + allowExecution = true, +}) { + const localize = useLocalize(); + const [isCopied, setIsCopied] = useState(false); + return ( +
+ {lang} + {plugin === true ? ( + + ) : ( +
+ {allowExecution === true && ( + + )} + -
- )} -
- ); - }, -); - -const FloatingCodeBar: React.FC = React.memo( - ({ lang, error, codeRef, blockIndex, plugin = null, allowExecution = true, isVisible }) => { - const localize = useLocalize(); - const [isCopied, setIsCopied] = useState(false); - const copyButtonRef = useRef(null); - - const handleCopy = useCallback(() => { - const codeString = codeRef.current?.textContent; - if (codeString != null) { - const wasFocused = document.activeElement === copyButtonRef.current; - setIsCopied(true); - copy(codeString.trim(), { format: 'text/plain' }); - if (wasFocused) { - requestAnimationFrame(() => { - copyButtonRef.current?.focus(); - }); - } - - setTimeout(() => { - const focusedElement = document.activeElement as HTMLElement | null; - setIsCopied(false); - requestAnimationFrame(() => { - focusedElement?.focus(); - }); - }, 3000); - } - }, [codeRef]); - - return ( -
- {plugin === true ? ( - - ) : ( - <> - {allowExecution === true && ( - - )} - - {isCopied ? ( -
- ); - }, -); + }} + > + {isCopied ? : } + {error !== true && ( + + {localize('com_ui_copy_code')} + + {isCopied ? localize('com_ui_copied') : localize('com_ui_copy_code')} + + + )} + +
+ )} +
+ ); +}); +CodeBar.displayName = 'CodeBar'; + +const FloatingCodeBar: React.FC = React.memo(function FloatingCodeBar({ + lang, + error, + codeRef, + blockIndex, + plugin = null, + allowExecution = true, + isVisible, +}) { + const localize = useLocalize(); + const [isCopied, setIsCopied] = useState(false); + const copyButtonRef = useRef(null); + + const handleCopy = useCallback(() => { + const codeString = codeRef.current?.textContent; + if (codeString != null) { + const wasFocused = document.activeElement === copyButtonRef.current; + setIsCopied(true); + copy(codeString.trim(), { format: 'text/plain' }); + if (wasFocused) { + requestAnimationFrame(() => { + copyButtonRef.current?.focus(); + }); + } + + setTimeout(() => { + const focusedElement = document.activeElement as HTMLElement | null; + setIsCopied(false); + requestAnimationFrame(() => { + focusedElement?.focus(); + }); + }, 3000); + } + }, [codeRef]); + + return ( +
+ {plugin === true ? ( + + ) : ( + <> + {allowExecution === true && ( + + )} + + {isCopied ? ( +
+ ); +}); +FloatingCodeBar.displayName = 'FloatingCodeBar'; const CodeBlock: React.FC = ({ lang, diff --git a/client/src/components/Messages/Content/Error.tsx b/client/src/components/Messages/Content/Error.tsx index 469e29fe32..ff2f2d7e90 100644 --- a/client/src/components/Messages/Content/Error.tsx +++ b/client/src/components/Messages/Content/Error.tsx @@ -41,6 +41,7 @@ const errorMessages = { [ErrorTypes.NO_USER_KEY]: 'com_error_no_user_key', [ErrorTypes.INVALID_USER_KEY]: 'com_error_invalid_user_key', [ErrorTypes.NO_BASE_URL]: 'com_error_no_base_url', + [ErrorTypes.INVALID_BASE_URL]: 'com_error_invalid_base_url', [ErrorTypes.INVALID_ACTION]: `com_error_${ErrorTypes.INVALID_ACTION}`, [ErrorTypes.INVALID_REQUEST]: `com_error_${ErrorTypes.INVALID_REQUEST}`, [ErrorTypes.REFUSAL]: 'com_error_refusal', diff --git a/client/src/components/Messages/Content/Mermaid.tsx b/client/src/components/Messages/Content/Mermaid.tsx index 9d830b3fdc..03037f4427 100644 --- a/client/src/components/Messages/Content/Mermaid.tsx +++ b/client/src/components/Messages/Content/Mermaid.tsx @@ -12,6 +12,7 @@ import { OGDialogContent, } from '@librechat/client'; import { useLocalize, useDebouncedMermaid } from '~/hooks'; +import { fixSubgraphTitleContrast } from '~/utils/mermaid'; import MermaidHeader from './MermaidHeader'; import cn from '~/utils/cn'; @@ -181,6 +182,8 @@ const Mermaid: React.FC = memo(({ children, id, theme }) => { svgElement.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); } + fixSubgraphTitleContrast(svgElement); + return { processedSvg: new XMLSerializer().serializeToString(doc), parsedDimensions: dimensions, @@ -672,7 +675,7 @@ const Mermaid: React.FC = memo(({ children, id, theme }) => { className={cn( 'relative overflow-hidden p-4 transition-colors duration-200', 'rounded-md', - showControls ? 'bg-surface-primary-alt' : 'bg-transparent', + showControls ? 'bg-surface-primary-alt dark:bg-white/[0.03]' : 'bg-transparent', isPanning ? 'cursor-grabbing' : 'cursor-grab', )} style={{ height: `${calculatedHeight}px` }} @@ -811,7 +814,7 @@ const Mermaid: React.FC = memo(({ children, id, theme }) => { className={cn( 'relative overflow-hidden p-4 transition-colors duration-200', 'rounded-md', - showControls ? 'bg-surface-primary-alt' : 'bg-transparent', + showControls ? 'bg-surface-primary-alt dark:bg-white/[0.03]' : 'bg-transparent', isPanning ? 'cursor-grabbing' : 'cursor-grab', )} style={{ height: `${calculatedHeight}px` }} diff --git a/client/src/components/Messages/Content/MermaidHeader.tsx b/client/src/components/Messages/Content/MermaidHeader.tsx index 03b49b6558..2d3a416a5a 100644 --- a/client/src/components/Messages/Content/MermaidHeader.tsx +++ b/client/src/components/Messages/Content/MermaidHeader.tsx @@ -1,7 +1,7 @@ import React, { memo, useState, useCallback, useRef } from 'react'; import copy from 'copy-to-clipboard'; import { Expand, ChevronUp, ChevronDown } from 'lucide-react'; -import { Button, Clipboard, CheckMark } from '@librechat/client'; +import { Clipboard, CheckMark, TooltipAnchor } from '@librechat/client'; import { useLocalize } from '~/hooks'; import cn from '~/utils/cn'; @@ -15,8 +15,8 @@ interface MermaidHeaderProps { onToggleCode: () => void; } -const buttonClasses = - 'h-auto gap-1 rounded-sm px-1 py-0 text-xs text-gray-200 hover:bg-gray-600 hover:text-white focus-visible:ring-white focus-visible:ring-offset-0'; +const iconBtnClass = + 'flex items-center justify-center rounded p-1.5 text-text-secondary hover:bg-surface-hover focus-visible:outline focus-visible:outline-white'; const MermaidHeader: React.FC = memo( ({ @@ -49,46 +49,58 @@ const MermaidHeader: React.FC = memo( return (
- {localize('com_ui_mermaid')} -
- {showExpandButton && onExpand && ( - + } + /> + )} + - - {localize('com_ui_expand')} - - )} - - -
+ {showCode ? : } + + } + /> + + {isCopied ? ( + + ) : ( + + )} + + } + />
); }, diff --git a/client/src/components/Messages/ContentRender.tsx b/client/src/components/Messages/ContentRender.tsx index 5724ff77c2..4114baefe4 100644 --- a/client/src/components/Messages/ContentRender.tsx +++ b/client/src/components/Messages/ContentRender.tsx @@ -22,176 +22,175 @@ type ContentRenderProps = { 'currentEditId' | 'setCurrentEditId' | 'siblingIdx' | 'setSiblingIdx' | 'siblingCount' >; -const ContentRender = memo( - ({ +const ContentRender = memo(function ContentRender({ + message: msg, + siblingIdx, + siblingCount, + setSiblingIdx, + currentEditId, + setCurrentEditId, + isSubmitting = false, +}: ContentRenderProps) { + const localize = useLocalize(); + const { attachments, searchResults } = useAttachments({ + messageId: msg?.messageId, + attachments: msg?.attachments, + }); + const { + edit, + index, + agent, + assistant, + enterEdit, + conversation, + messageLabel, + handleContinue, + handleFeedback, + latestMessageId, + copyToClipboard, + regenerateMessage, + latestMessageDepth, + } = useMessageActions({ message: msg, - siblingIdx, - siblingCount, - setSiblingIdx, + searchResults, currentEditId, setCurrentEditId, - isSubmitting = false, - }: ContentRenderProps) => { - const localize = useLocalize(); - const { attachments, searchResults } = useAttachments({ - messageId: msg?.messageId, - attachments: msg?.attachments, - }); - const { - edit, - index, - agent, - assistant, - enterEdit, - conversation, + }); + const fontSize = useAtomValue(fontSizeAtom); + const maximizeChatSpace = useRecoilValue(store.maximizeChatSpace); + + const handleRegenerateMessage = useCallback(() => regenerateMessage(), [regenerateMessage]); + const isLast = useMemo( + () => !(msg?.children?.length ?? 0) && (msg?.depth === latestMessageDepth || msg?.depth === -1), + [msg?.children, msg?.depth, latestMessageDepth], + ); + const hasNoChildren = !(msg?.children?.length ?? 0); + const isLatestMessage = msg?.messageId === latestMessageId; + /** Only pass isSubmitting to the latest message to prevent unnecessary re-renders */ + const effectiveIsSubmitting = isLatestMessage ? isSubmitting : false; + + const iconData: TMessageIcon = useMemo( + () => ({ + endpoint: msg?.endpoint ?? conversation?.endpoint, + model: msg?.model ?? conversation?.model, + iconURL: msg?.iconURL, + modelLabel: messageLabel, + isCreatedByUser: msg?.isCreatedByUser, + }), + [ messageLabel, - latestMessage, - handleContinue, - handleFeedback, - copyToClipboard, - regenerateMessage, - } = useMessageActions({ - message: msg, - searchResults, - currentEditId, - setCurrentEditId, - }); - const fontSize = useAtomValue(fontSizeAtom); - const maximizeChatSpace = useRecoilValue(store.maximizeChatSpace); + conversation?.endpoint, + conversation?.model, + msg?.model, + msg?.iconURL, + msg?.endpoint, + msg?.isCreatedByUser, + ], + ); - const handleRegenerateMessage = useCallback(() => regenerateMessage(), [regenerateMessage]); - const isLast = useMemo( - () => - !(msg?.children?.length ?? 0) && (msg?.depth === latestMessage?.depth || msg?.depth === -1), - [msg?.children, msg?.depth, latestMessage?.depth], - ); - const hasNoChildren = !(msg?.children?.length ?? 0); - const isLatestMessage = msg?.messageId === latestMessage?.messageId; - /** Only pass isSubmitting to the latest message to prevent unnecessary re-renders */ - const effectiveIsSubmitting = isLatestMessage ? isSubmitting : false; + const { hasParallelContent } = useContentMetadata(msg); - const iconData: TMessageIcon = useMemo( - () => ({ - endpoint: msg?.endpoint ?? conversation?.endpoint, - model: msg?.model ?? conversation?.model, - iconURL: msg?.iconURL, - modelLabel: messageLabel, - isCreatedByUser: msg?.isCreatedByUser, - }), - [ - messageLabel, - conversation?.endpoint, - conversation?.model, - msg?.model, - msg?.iconURL, - msg?.endpoint, - msg?.isCreatedByUser, - ], - ); + if (!msg) { + return null; + } - const { hasParallelContent } = useContentMetadata(msg); - - if (!msg) { - return null; + const getChatWidthClass = () => { + if (maximizeChatSpace) { + return 'w-full max-w-full md:px-5 lg:px-1 xl:px-5'; } + if (hasParallelContent) { + return 'md:max-w-[58rem] xl:max-w-[70rem]'; + } + return 'md:max-w-[47rem] xl:max-w-[55rem]'; + }; - const getChatWidthClass = () => { - if (maximizeChatSpace) { - return 'w-full max-w-full md:px-5 lg:px-1 xl:px-5'; - } - if (hasParallelContent) { - return 'md:max-w-[58rem] xl:max-w-[70rem]'; - } - return 'md:max-w-[47rem] xl:max-w-[55rem]'; - }; + const baseClasses = { + common: 'group mx-auto flex flex-1 gap-3 transition-all duration-300 transform-gpu ', + chat: getChatWidthClass(), + }; - const baseClasses = { - common: 'group mx-auto flex flex-1 gap-3 transition-all duration-300 transform-gpu ', - chat: getChatWidthClass(), - }; + const conditionalClasses = { + focus: 'focus:outline-none focus:ring-2 focus:ring-border-xheavy', + }; - const conditionalClasses = { - focus: 'focus:outline-none focus:ring-2 focus:ring-border-xheavy', - }; + return ( +
+ {!hasParallelContent && ( +
+
+ +
+
+ )} - return (
{!hasParallelContent && ( -
-
- -
-
+

{messageLabel}

)} -
- {!hasParallelContent && ( -

{messageLabel}

- )} - -
-
- } - /> -
- {hasNoChildren && effectiveIsSubmitting ? ( - - ) : ( - - - - - )} +
+
+ } + />
+ {hasNoChildren && effectiveIsSubmitting ? ( + + ) : ( + + + + + )}
- ); - }, -); +
+ ); +}); +ContentRender.displayName = 'ContentRender'; export default ContentRender; diff --git a/client/src/components/Messages/MessageContent.tsx b/client/src/components/Messages/MessageContent.tsx index 68fe2d8629..0e53b1c840 100644 --- a/client/src/components/Messages/MessageContent.tsx +++ b/client/src/components/Messages/MessageContent.tsx @@ -5,25 +5,23 @@ import type { TMessageProps } from '~/common'; import MultiMessage from '~/components/Chat/Messages/MultiMessage'; import ContentRender from './ContentRender'; -const MessageContainer = React.memo( - ({ - handleScroll, - children, - }: { - handleScroll: (event?: unknown) => void; - children: React.ReactNode; - }) => { - return ( -
- {children} -
- ); - }, -); +const MessageContainer = React.memo(function MessageContainer({ + handleScroll, + children, +}: { + handleScroll: (event?: unknown) => void; + children: React.ReactNode; +}) { + return ( +
+ {children} +
+ ); +}); export default function MessageContent(props: TMessageProps) { const { conversation, handleScroll, isSubmitting } = useMessageProcess({ diff --git a/client/src/components/Nav/AccountSettings.tsx b/client/src/components/Nav/AccountSettings.tsx index e3f97160eb..cf80f89ca2 100644 --- a/client/src/components/Nav/AccountSettings.tsx +++ b/client/src/components/Nav/AccountSettings.tsx @@ -1,5 +1,5 @@ import { useState, memo, useRef } from 'react'; -import * as Select from '@ariakit/react/select'; +import * as Menu from '@ariakit/react/menu'; import { FileText, LogOut } from 'lucide-react'; import { LinkIcon, GearIcon, DropdownMenuSeparator, Avatar } from '@librechat/client'; import { MyFilesModal } from '~/components/Chat/Input/Files/MyFilesModal'; @@ -20,8 +20,8 @@ function AccountSettings() { const accountSettingsButtonRef = useRef(null); return ( - - + {user?.name ?? user?.username ?? localize('com_nav_user')}
- - + )} - setShowFiles(true)} - className="select-item text-sm" - > + setShowFiles(true)} className="select-item text-sm"> + {startupConfig?.helpAndFaqURL !== '/' && ( - window.open(startupConfig?.helpAndFaqURL, '_blank')} className="select-item text-sm" > + )} - setShowSettings(true)} - className="select-item text-sm" - > + setShowSettings(true)} className="select-item text-sm"> + - logout()} - value="logout" - className="select-item text-sm" - > + logout()} className="select-item text-sm"> - + + {showFiles && ( )} {showSettings && } - + ); } diff --git a/client/src/components/Nav/Favorites/FavoriteItem.tsx b/client/src/components/Nav/Favorites/FavoriteItem.tsx index 173be27d00..248008869d 100644 --- a/client/src/components/Nav/Favorites/FavoriteItem.tsx +++ b/client/src/components/Nav/Favorites/FavoriteItem.tsx @@ -126,8 +126,8 @@ export default function FavoriteItem({ className={cn( 'absolute right-2 flex items-center', isPopoverActive - ? 'opacity-100' - : 'opacity-0 group-focus-within:opacity-100 group-hover:opacity-100', + ? 'pointer-events-auto opacity-100' + : 'pointer-events-none opacity-0 group-focus-within:pointer-events-auto group-focus-within:opacity-100 group-hover:pointer-events-auto group-hover:opacity-100', )} onClick={(e) => e.stopPropagation()} > diff --git a/client/src/components/Nav/Favorites/FavoritesList.tsx b/client/src/components/Nav/Favorites/FavoritesList.tsx index 86fe4a793f..82225733fd 100644 --- a/client/src/components/Nav/Favorites/FavoritesList.tsx +++ b/client/src/components/Nav/Favorites/FavoritesList.tsx @@ -1,15 +1,21 @@ import React, { useRef, useCallback, useMemo, useEffect } from 'react'; -import { useRecoilValue } from 'recoil'; import { LayoutGrid } from 'lucide-react'; import { useDrag, useDrop } from 'react-dnd'; import { Skeleton } from '@librechat/client'; import { useNavigate } from 'react-router-dom'; import { useQueries } from '@tanstack/react-query'; +import { useRecoilValue } from 'recoil'; import { QueryKeys, dataService } from 'librechat-data-provider'; import type t from 'librechat-data-provider'; -import { useFavorites, useLocalize, useShowMarketplace, useNewConvo } from '~/hooks'; -import { useAssistantsMapContext, useAgentsMapContext } from '~/Providers'; import type { AgentQueryResult } from '~/common'; +import { + useGetConversation, + useShowMarketplace, + useFavorites, + useLocalize, + useNewConvo, +} from '~/hooks'; +import { useAssistantsMapContext, useAgentsMapContext } from '~/Providers'; import useSelectMention from '~/hooks/Input/useSelectMention'; import { useGetEndpointsQuery } from '~/data-provider'; import FavoriteItem from './FavoriteItem'; @@ -122,20 +128,20 @@ export default function FavoritesList({ const navigate = useNavigate(); const localize = useLocalize(); const search = useRecoilValue(store.search); + const getConversation = useGetConversation(0); const { favorites, reorderFavorites, isLoading: isFavoritesLoading } = useFavorites(); const showAgentMarketplace = useShowMarketplace(); const { newConversation } = useNewConvo(); const assistantsMap = useAssistantsMapContext(); const agentsMap = useAgentsMapContext(); - const conversation = useRecoilValue(store.conversationByIndex(0)); const { data: endpointsConfig = {} as t.TEndpointsConfig } = useGetEndpointsQuery(); const { onSelectEndpoint } = useSelectMention({ modelSpecs: [], - conversation, assistantsMap, endpointsConfig, + getConversation, newConversation, returnHandlers: true, }); diff --git a/client/src/components/Nav/Favorites/tests/FavoritesList.spec.tsx b/client/src/components/Nav/Favorites/tests/FavoritesList.spec.tsx index 8318b94698..ed71221de3 100644 --- a/client/src/components/Nav/Favorites/tests/FavoritesList.spec.tsx +++ b/client/src/components/Nav/Favorites/tests/FavoritesList.spec.tsx @@ -56,6 +56,7 @@ jest.mock('~/hooks', () => ({ useLocalize: () => (key: string) => key, useShowMarketplace: () => false, useNewConvo: () => ({ newConversation: jest.fn() }), + useGetConversation: () => () => null, })); jest.mock('~/Providers', () => ({ diff --git a/client/src/components/Nav/Nav.tsx b/client/src/components/Nav/Nav.tsx index 74883b94f4..cdee938663 100644 --- a/client/src/components/Nav/Nav.tsx +++ b/client/src/components/Nav/Nav.tsx @@ -225,6 +225,7 @@ const Nav = memo( aria-label={localize('com_ui_chat_history')} className="flex h-full flex-col px-2 pb-3.5" aria-hidden={!navVisible} + {...{ inert: !navVisible ? '' : undefined }} >
= useCallback( + const clickHandler: React.MouseEventHandler = useCallback( (e) => { - if (e.button === 0 && (e.ctrlKey || e.metaKey)) { - window.open('/c/new', '_blank'); + // Let browser handle modified/non-left clicks (new tab, context menu, etc.) + if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) { return; } + + e.preventDefault(); clearMessagesCache(queryClient, conversation?.conversationId); queryClient.invalidateQueries([QueryKeys.messages]); newConvo(); - navigate('/c/new', { state: { focusChat: true } }); if (isSmallScreen) { toggleNav(); } }, - [queryClient, conversation, newConvo, navigate, toggleNav, isSmallScreen], + [queryClient, conversation, newConvo, toggleNav, isSmallScreen], ); return ( @@ -84,14 +84,16 @@ export default function NewChat({ description={localize('com_ui_new_chat')} render={ } /> diff --git a/client/src/components/Nav/SettingsTabs/Account/BackupCodesItem.tsx b/client/src/components/Nav/SettingsTabs/Account/BackupCodesItem.tsx index c89ce61fff..e66cb7b08a 100644 --- a/client/src/components/Nav/SettingsTabs/Account/BackupCodesItem.tsx +++ b/client/src/components/Nav/SettingsTabs/Account/BackupCodesItem.tsx @@ -1,12 +1,23 @@ import React, { useState } from 'react'; import { RefreshCcw } from 'lucide-react'; +import { useSetRecoilState } from 'recoil'; import { motion, AnimatePresence } from 'framer-motion'; -import { TBackupCode, TRegenerateBackupCodesResponse, type TUser } from 'librechat-data-provider'; +import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from 'input-otp'; +import type { + TRegenerateBackupCodesResponse, + TRegenerateBackupCodesRequest, + TBackupCode, + TUser, +} from 'librechat-data-provider'; import { - OGDialog, + InputOTPSeparator, + InputOTPGroup, + InputOTPSlot, OGDialogContent, OGDialogTitle, OGDialogTrigger, + OGDialog, + InputOTP, Button, Label, Spinner, @@ -15,7 +26,6 @@ import { } from '@librechat/client'; import { useRegenerateBackupCodesMutation } from '~/data-provider'; import { useAuthContext, useLocalize } from '~/hooks'; -import { useSetRecoilState } from 'recoil'; import store from '~/store'; const BackupCodesItem: React.FC = () => { @@ -24,25 +34,30 @@ const BackupCodesItem: React.FC = () => { const { showToast } = useToastContext(); const setUser = useSetRecoilState(store.user); const [isDialogOpen, setDialogOpen] = useState(false); + const [otpToken, setOtpToken] = useState(''); + const [useBackup, setUseBackup] = useState(false); const { mutate: regenerateBackupCodes, isLoading } = useRegenerateBackupCodesMutation(); + const needs2FA = !!user?.twoFactorEnabled; + const fetchBackupCodes = (auto: boolean = false) => { - regenerateBackupCodes(undefined, { + let payload: TRegenerateBackupCodesRequest | undefined; + if (needs2FA && otpToken.trim()) { + payload = useBackup ? { backupCode: otpToken.trim() } : { token: otpToken.trim() }; + } + + regenerateBackupCodes(payload, { onSuccess: (data: TRegenerateBackupCodesResponse) => { - const newBackupCodes: TBackupCode[] = data.backupCodesHash.map((codeHash) => ({ - codeHash, - used: false, - usedAt: null, - })); + const newBackupCodes: TBackupCode[] = data.backupCodesHash; setUser((prev) => ({ ...prev, backupCodes: newBackupCodes }) as TUser); + setOtpToken(''); showToast({ message: localize('com_ui_backup_codes_regenerated'), status: 'success', }); - // Trigger file download only when user explicitly clicks the button. if (!auto && newBackupCodes.length) { const codesString = data.backupCodes.join('\n'); const blob = new Blob([codesString], { type: 'text/plain;charset=utf-8' }); @@ -66,6 +81,8 @@ const BackupCodesItem: React.FC = () => { fetchBackupCodes(false); }; + const otpReady = !needs2FA || otpToken.length === (useBackup ? 8 : 6); + return (
@@ -161,10 +178,10 @@ const BackupCodesItem: React.FC = () => { ); })}
-
+
)} + {needs2FA && ( +
+ +
+ + {useBackup ? ( + + + + + + + + + + + ) : ( + <> + + + + + + + + + + + + + )} + +
+ +
+ )} diff --git a/client/src/components/Nav/SettingsTabs/Account/DeleteAccount.tsx b/client/src/components/Nav/SettingsTabs/Account/DeleteAccount.tsx index e879a0f2c6..d9c432c6a2 100644 --- a/client/src/components/Nav/SettingsTabs/Account/DeleteAccount.tsx +++ b/client/src/components/Nav/SettingsTabs/Account/DeleteAccount.tsx @@ -1,16 +1,22 @@ -import { LockIcon, Trash } from 'lucide-react'; import React, { useState, useCallback } from 'react'; +import { LockIcon, Trash } from 'lucide-react'; +import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from 'input-otp'; import { - Label, - Input, - Button, - Spinner, - OGDialog, + InputOTPSeparator, OGDialogContent, OGDialogTrigger, OGDialogHeader, + InputOTPGroup, OGDialogTitle, + InputOTPSlot, + OGDialog, + InputOTP, + Spinner, + Button, + Label, + Input, } from '@librechat/client'; +import type { TDeleteUserRequest } from 'librechat-data-provider'; import { useDeleteUserMutation } from '~/data-provider'; import { useAuthContext } from '~/hooks/AuthContext'; import { LocalizeFunction } from '~/common'; @@ -21,16 +27,27 @@ const DeleteAccount = ({ disabled = false }: { title?: string; disabled?: boolea const localize = useLocalize(); const { user, logout } = useAuthContext(); const { mutate: deleteUser, isLoading: isDeleting } = useDeleteUserMutation({ - onMutate: () => logout(), + onSuccess: () => logout(), }); const [isDialogOpen, setDialogOpen] = useState(false); const [isLocked, setIsLocked] = useState(true); + const [otpToken, setOtpToken] = useState(''); + const [useBackup, setUseBackup] = useState(false); + + const needs2FA = !!user?.twoFactorEnabled; const handleDeleteUser = () => { - if (!isLocked) { - deleteUser(undefined); + if (isLocked) { + return; } + + let payload: TDeleteUserRequest | undefined; + if (needs2FA && otpToken.trim()) { + payload = useBackup ? { backupCode: otpToken.trim() } : { token: otpToken.trim() }; + } + + deleteUser(payload); }; const handleInputChange = useCallback( @@ -42,6 +59,8 @@ const DeleteAccount = ({ disabled = false }: { title?: string; disabled?: boolea [user?.email], ); + const otpReady = !needs2FA || otpToken.length === (useBackup ? 8 : 6); + return ( <> @@ -79,7 +98,60 @@ const DeleteAccount = ({ disabled = false }: { title?: string; disabled?: boolea (e) => handleInputChange(e.target.value), )}
- {renderDeleteButton(handleDeleteUser, isDeleting, isLocked, localize)} + {needs2FA && ( +
+ +
+ + {useBackup ? ( + + + + + + + + + + + ) : ( + <> + + + + + + + + + + + + + )} + +
+ +
+ )} + {renderDeleteButton(handleDeleteUser, isDeleting, isLocked || !otpReady, localize)}
diff --git a/client/src/components/Plugins/Store/PluginAuthForm.tsx b/client/src/components/Plugins/Store/PluginAuthForm.tsx index 5af1948c11..d304b2eab7 100644 --- a/client/src/components/Plugins/Store/PluginAuthForm.tsx +++ b/client/src/components/Plugins/Store/PluginAuthForm.tsx @@ -20,6 +20,7 @@ function PluginAuthForm({ plugin, onSubmit, isEntityTool }: TPluginAuthFormProps const localize = useLocalize(); const authConfig = plugin?.authConfig ?? []; + const allFieldsOptional = authConfig.length > 0 && authConfig.every((c) => c.optional === true); return (
@@ -38,6 +39,7 @@ function PluginAuthForm({ plugin, onSubmit, isEntityTool }: TPluginAuthFormProps > {authConfig.map((config: TPluginAuthConfig, i: number) => { const authField = config.authField.split('||')[0]; + const isOptional = config.optional === true; return (
- {isGlobalGroup && ( + {isPublicGroup && ( } = useGetStartupConfig(); - const { instanceProjectId } = startupConfig; const hasCreateAccess = useHasAccess({ permissionType: PermissionTypes.PROMPTS, permission: Permissions.CREATE, @@ -73,17 +70,9 @@ export default function List({ )} {groups.map((group) => { if (isChatRoute) { - return ( - - ); + return ; } - return ( - - ); + return ; })}
diff --git a/client/src/components/Share/Message.tsx b/client/src/components/Share/Message.tsx index 99d46954a8..8e2c7796e3 100644 --- a/client/src/components/Share/Message.tsx +++ b/client/src/components/Share/Message.tsx @@ -69,7 +69,7 @@ export default function Message(props: TMessageProps) { >
{messageLabel}
-
+
(-1); return ( -
+
-
- {(_messagesTree && _messagesTree.length == 0) || _messagesTree === null ? ( +
+ {(_messagesTree && _messagesTree.length === 0) || _messagesTree === null ? (
{localize('com_ui_nothing_found')}
diff --git a/client/src/components/Share/ShareMessagesProvider.tsx b/client/src/components/Share/ShareMessagesProvider.tsx index e87591a082..e614aa891a 100644 --- a/client/src/components/Share/ShareMessagesProvider.tsx +++ b/client/src/components/Share/ShareMessagesProvider.tsx @@ -25,7 +25,8 @@ export function ShareMessagesProvider({ messages, children }: ShareMessagesProvi ask: () => Promise.resolve(), regenerate: () => {}, handleContinue: () => {}, - latestMessage: messages[messages.length - 1] ?? null, + latestMessageId: messages[messages.length - 1]?.messageId, + latestMessageDepth: messages[messages.length - 1]?.depth, isSubmitting: false, abortScroll: false, setAbortScroll: () => {}, diff --git a/client/src/components/Share/ShareView.tsx b/client/src/components/Share/ShareView.tsx index 99ab7f35eb..00a0d36398 100644 --- a/client/src/components/Share/ShareView.tsx +++ b/client/src/components/Share/ShareView.tsx @@ -123,14 +123,14 @@ function SharedView() { } const footer = ( -
-