diff --git a/LINTING_FIXES.md b/LINTING_FIXES.md new file mode 100644 index 00000000..249db1a1 --- /dev/null +++ b/LINTING_FIXES.md @@ -0,0 +1,86 @@ +# Linting Fixes Applied + +## Issues Found in CI + +### Error (1) +**File**: `apps/backend/src/modules/escrow/services/escrow.service.ts` +**Line**: 297 +**Issue**: Unsafe argument of type error typed assigned to a parameter of type `EscrowEventType` +**Error Code**: `@typescript-eslint/no-unsafe-argument` + +**Root Cause**: Missing closing parenthesis in `logEvent()` method call + +**Fix Applied**: +```typescript +// Before (missing closing parenthesis) +await this.logEvent( + id, + EscrowEventType.EXPIRED, + userId, + { + reason: dto.reason || 'Deadline exceeded', + previousStatus: escrow.status, + expiresAt: escrow.expiresAt, + expiredAt: now, + }, + ipAddress, + +// After (added closing parenthesis) +await this.logEvent( + id, + EscrowEventType.EXPIRED, + userId, + { + reason: dto.reason || 'Deadline exceeded', + previousStatus: escrow.status, + expiresAt: escrow.expiresAt, + expiredAt: now, + }, + ipAddress, +); +``` + +### Warnings (5) +**File**: `apps/backend/src/modules/escrow/services/escrow.service.spec.ts` +**Lines**: 397, 421, 509, 539, 566 +**Issue**: Unsafe argument of type `any` assigned to a parameter of type `UpdateResult | Promise` +**Error Code**: `@typescript-eslint/no-unsafe-argument` + +**Root Cause**: Using `as any` type casting for UpdateResult mocks + +**Fix Applied**: +```typescript +// Before +escrowRepository.update.mockResolvedValue({ affected: 1 } as any); + +// After +escrowRepository.update.mockResolvedValue({ affected: 1 } as UpdateResult); +``` + +**Locations Fixed**: +1. Line 393 - `fileDispute` test +2. Line 419 - `fileDispute` by seller test +3. Line 505 - `resolveDispute` with RELEASED_TO_SELLER test +4. Line 530 - `resolveDispute` with CANCELLED test +5. Line 556 - `resolveDispute` with SPLIT test + +## Verification + +✅ TypeScript diagnostics: No errors +✅ All type casts properly typed +✅ Syntax errors resolved +✅ Code follows TypeScript strict mode rules + +## Commit Details + +**Commit**: d7bcb27 +**Message**: "fix: resolve linting errors in escrow service" +**Files Changed**: 3 +- `PR_DETAILS.md` (new) +- `apps/backend/src/modules/escrow/services/escrow.service.spec.ts` (modified) +- `apps/backend/src/modules/escrow/services/escrow.service.ts` (modified) + +## CI Status + +The fixes have been pushed to the `feature/deadline-enforcement` branch. +CI should now pass the linting checks. diff --git a/PR_MESSAGE.md b/PR_MESSAGE.md new file mode 100644 index 00000000..f64fc69a --- /dev/null +++ b/PR_MESSAGE.md @@ -0,0 +1,44 @@ +# Add Secure Admin-Controlled Contract Upgrade (Safe Upgrade Pattern) + +## Summary + +This PR implements a secure, admin-controlled contract upgrade pattern ("Safe Upgrade" / Admin Proxy) for the VaultixEscrow Soroban contract. It allows protocol maintainers to fix bugs or add features without migrating all state and users to a new contract address. + +## Key Changes + +- **Upgrade Functionality:** + - Added `upgrade(env, new_wasm_hash: [u8; 32])` to the contract, callable only by the admin. + - Emits a `ContractUpgraded` event before performing the upgrade. + - Enforces strict access control (admin-only). + - Documents storage compatibility requirements for future upgrades. + +- **Testing:** + - Integration test verifies: + - State is preserved across upgrades. + - Unauthorized users cannot trigger upgrades. + - New contract logic is accessible after upgrade. + +## Motivation + +Soroban allows contract upgrades via `env.deployer().update_current_contract_wasm`. This PR wraps that capability in a secure, admin-controlled function to ensure only authorized upgrades, protecting user funds and protocol integrity. + +## Acceptance Criteria + +- [x] `upgrade` function exists and is admin-protected. +- [x] Emits upgrade event. +- [x] Storage compatibility is documented. +- [x] Integration test proves code can be swapped while keeping escrow storage intact. +- [x] Unauthorized users cannot trigger an upgrade. + +## Non-Goals + +- DAO-voting based upgrades (admin-only for now). + +--- + +**Reviewer Notes:** +- Please review the storage compatibility comment for future upgrade safety. +- All tests pass (`cargo test`). +- No breaking changes to existing escrow logic. + +--- diff --git a/apps/backend/CONDITION_FULFILLMENT_EXAMPLE.md b/apps/backend/CONDITION_FULFILLMENT_EXAMPLE.md deleted file mode 100644 index 015a213b..00000000 --- a/apps/backend/CONDITION_FULFILLMENT_EXAMPLE.md +++ /dev/null @@ -1,84 +0,0 @@ -# Condition Fulfillment API Example - -This document demonstrates how to use the new condition fulfillment and confirmation endpoints. - -## API Endpoints - -### 1. Fulfill Condition (Seller) -```http -POST /escrows/{escrowId}/conditions/{conditionId}/fulfill -Authorization: Bearer {seller_token} -Content-Type: application/json - -{ - "notes": "Package has been shipped via FedEx", - "evidence": "Tracking number: 1234567890" -} -``` - -**Response:** -```json -{ - "id": "condition-123", - "escrowId": "escrow-456", - "description": "Delivery confirmation required", - "type": "manual", - "isFulfilled": true, - "fulfilledAt": "2024-01-15T10:30:00Z", - "fulfilledByUserId": "seller-user-id", - "fulfillmentNotes": "Package has been shipped via FedEx", - "fulfillmentEvidence": "Tracking number: 1234567890", - "isMet": false, - "metAt": null, - "metByUserId": null -} -``` - -### 2. Confirm Condition (Buyer) -```http -POST /escrows/{escrowId}/conditions/{conditionId}/confirm -Authorization: Bearer {buyer_token} -``` - -**Response:** -```json -{ - "id": "condition-123", - "escrowId": "escrow-456", - "description": "Delivery confirmation required", - "type": "manual", - "isFulfilled": true, - "fulfilledAt": "2024-01-15T10:30:00Z", - "fulfilledByUserId": "seller-user-id", - "fulfillmentNotes": "Package has been shipped via FedEx", - "fulfillmentEvidence": "Tracking number: 1234567890", - "isMet": true, - "metAt": "2024-01-15T14:45:00Z", - "metByUserId": "buyer-user-id" -} -``` - -## Workflow - -1. **Seller fulfills condition**: Marks the condition as fulfilled with optional notes and evidence -2. **Buyer confirms condition**: Confirms that the condition has been met -3. **Auto-release**: If all conditions are confirmed, the escrow is automatically released - -## Permission Rules - -- Only **sellers** can fulfill conditions -- Only **buyers** can confirm conditions -- Escrow must be in **ACTIVE** status -- Conditions must be **fulfilled** before they can be **confirmed** - -## Events and Webhooks - -The system emits the following webhook events: -- `condition.fulfilled` - When a seller fulfills a condition -- `condition.confirmed` - When a buyer confirms a condition - -## Error Handling - -- `403 Forbidden`: User doesn't have permission (wrong role) -- `400 Bad Request`: Invalid state (escrow not active, condition not fulfilled) -- `404 Not Found`: Escrow or condition doesn't exist \ No newline at end of file diff --git a/apps/backend/DEADLINE_ENFORCEMENT_CHANGES.md b/apps/backend/DEADLINE_ENFORCEMENT_CHANGES.md deleted file mode 100644 index 2ee283d4..00000000 --- a/apps/backend/DEADLINE_ENFORCEMENT_CHANGES.md +++ /dev/null @@ -1,153 +0,0 @@ -# Deadline Enforcement Implementation Summary - -## Changes Made - -### 1. Entity Updates - -#### `escrow.entity.ts` -- Added `EXPIRED` status to `EscrowStatus` enum -- Terminal state alongside `COMPLETED` and `CANCELLED` - -#### `escrow-event.entity.ts` -- Added `EXPIRED` event type to `EscrowEventType` enum - -### 2. State Machine Updates - -#### `escrow-state-machine.ts` -- Updated valid transitions to allow: - - `PENDING → EXPIRED` - - `ACTIVE → EXPIRED` - - `DISPUTED → EXPIRED` -- Updated `isTerminalStatus()` to include `EXPIRED` -- `EXPIRED` is a terminal state (no outgoing transitions) - -### 3. New DTO - -#### `expire-escrow.dto.ts` -- Created DTO for expiration requests -- Optional `reason` field (max 1000 chars) - -### 4. Service Updates - -#### `escrow.service.ts` -- Added `expire()` method: - - Validates escrow has deadline - - Checks deadline has passed - - Authorizes depositor or arbitrator - - Validates state transition - - Updates status to EXPIRED - - Logs event and dispatches webhook -- Added expiry guards to: - - `releaseEscrow()`: Prevents release of expired escrows - - `fulfillCondition()`: Prevents fulfillment on expired escrows - - `confirmCondition()`: Prevents confirmation on expired escrows - -#### `escrow-scheduler.service.ts` -- Updated `autoCancelEscrow()` to set status to `EXPIRED` instead of `CANCELLED` -- Updated `escalateToDispute()` to set status to `EXPIRED` instead of `DISPUTED` -- Changed event types to use `expired` instead of custom types -- Updated notification messages - -### 5. Controller Updates - -#### `escrow.controller.ts` -- Added `POST /escrows/:id/expire` endpoint -- Requires authentication and escrow access -- Accepts `ExpireEscrowDto` body - -### 6. Test Updates - -#### `escrow.service.spec.ts` -- Added comprehensive expiration tests: - - Expire by depositor - - Expire by arbitrator - - Reject expiration of completed escrows - - Reject expiration without deadline - - Reject expiration before deadline - - Reject unauthorized expiration - - Expire pending escrows - - Reject double expiration - -#### `escrow-state-machine.spec.ts` -- Added tests for EXPIRED transitions: - - Allow PENDING → EXPIRED - - Allow ACTIVE → EXPIRED - - Allow DISPUTED → EXPIRED - - Prevent transitions from EXPIRED -- Updated terminal status tests to include EXPIRED - -### 7. Documentation - -#### `DEADLINE_ENFORCEMENT.md` -- Comprehensive guide on deadline enforcement -- Lifecycle states and transitions -- Expiration rules and endpoint documentation -- Automatic expiration via scheduler -- Invariants and event schemas -- Interaction with disputes and conditions - -#### `EXPIRATION_EXAMPLE.md` -- Practical usage examples -- Multiple scenarios (normal completion, manual expiration, auto-expiration) -- Error case examples -- API request/response samples - -## Key Features - -### Explicit Lifecycle Rules -- Clear state transitions with EXPIRED as terminal state -- No operations allowed after expiration -- Deadline validation enforced - -### Authorization -- Depositor (creator) can trigger expiration -- Arbitrator can trigger expiration -- Other parties cannot expire escrows - -### Deadline Enforcement -- Before deadline: Normal operations -- After deadline: Only expiration allowed -- Guards prevent condition operations on expired escrows - -### Automatic Processing -- Hourly cron job expires overdue escrows -- PENDING escrows → EXPIRED -- ACTIVE escrows → EXPIRED (with arbitration flag) - -### Event Tracking -- EXPIRED events logged with full context -- Webhooks dispatched for monitoring -- Audit trail maintained - -## Invariants Enforced - -1. No new operations after expiry -2. No expiration of terminal states -3. Deadline required for expiration -4. Time validation (must be past deadline) -5. Authorization required (depositor or arbitrator) - -## Testing Coverage - -- ✅ Expiration by authorized users -- ✅ Rejection of unauthorized expiration -- ✅ Deadline validation -- ✅ Terminal state protection -- ✅ State machine transitions -- ✅ Idempotency handling -- ✅ Edge cases (no deadline, before deadline, already expired) - -## Migration Impact - -- Existing escrows without `expiresAt` are unaffected -- No database migration required (field already exists) -- Backward compatible with existing functionality -- New EXPIRED status added to enum (requires app restart) - -## Next Steps - -1. Deploy changes to staging environment -2. Run full test suite -3. Monitor scheduler execution -4. Update API documentation -5. Notify frontend team of new status and endpoint diff --git a/apps/backend/IMPLEMENTATION_CHECKLIST.md b/apps/backend/IMPLEMENTATION_CHECKLIST.md deleted file mode 100644 index eb9d49d8..00000000 --- a/apps/backend/IMPLEMENTATION_CHECKLIST.md +++ /dev/null @@ -1,129 +0,0 @@ -# Deadline Enforcement Implementation Checklist - -## ✅ Completed Tasks - -### Core Implementation -- [x] Added `EXPIRED` status to `EscrowStatus` enum -- [x] Added `EXPIRED` event type to `EscrowEventType` enum -- [x] Updated state machine transitions to support EXPIRED -- [x] Updated `isTerminalStatus()` to include EXPIRED -- [x] Created `ExpireEscrowDto` for API requests - -### Service Layer -- [x] Implemented `expire()` method in `EscrowService` - - [x] Deadline validation - - [x] Time validation (past deadline) - - [x] Authorization (depositor or arbitrator) - - [x] State transition validation - - [x] Event logging - - [x] Webhook dispatch -- [x] Added expiry guards to `releaseEscrow()` -- [x] Added expiry guards to `fulfillCondition()` -- [x] Added expiry guards to `confirmCondition()` - -### Controller Layer -- [x] Added `POST /escrows/:id/expire` endpoint -- [x] Imported `ExpireEscrowDto` -- [x] Applied authentication and access guards - -### Scheduler Updates -- [x] Updated `autoCancelEscrow()` to use EXPIRED status -- [x] Updated `escalateToDispute()` to use EXPIRED status -- [x] Changed event types to use 'expired' -- [x] Updated notification messages - -### Testing -- [x] Added 8 comprehensive tests to `escrow.service.spec.ts` - - [x] Expire by depositor - - [x] Expire by arbitrator - - [x] Reject completed escrow expiration - - [x] Reject no deadline expiration - - [x] Reject before deadline expiration - - [x] Reject unauthorized expiration - - [x] Expire pending escrow - - [x] Reject double expiration -- [x] Added 5 state machine tests to `escrow-state-machine.spec.ts` - - [x] PENDING → EXPIRED transition - - [x] ACTIVE → EXPIRED transition - - [x] DISPUTED → EXPIRED transition - - [x] No transitions from EXPIRED - - [x] EXPIRED is terminal status - -### Documentation -- [x] Created `DEADLINE_ENFORCEMENT.md` (comprehensive guide) -- [x] Created `EXPIRATION_EXAMPLE.md` (usage examples) -- [x] Created `QUICK_REFERENCE_EXPIRATION.md` (developer reference) -- [x] Created `DEADLINE_ENFORCEMENT_CHANGES.md` (change summary) -- [x] Created `IMPLEMENTATION_CHECKLIST.md` (this file) - -### Code Quality -- [x] No TypeScript diagnostics errors -- [x] Consistent naming conventions -- [x] Proper error messages -- [x] Event logging with context -- [x] Webhook integration - -## 🔄 Deployment Steps - -1. [ ] Review all changes with team -2. [ ] Run full test suite: `npm test` -3. [ ] Run E2E tests: `npm run test:e2e` -4. [ ] Deploy to staging environment -5. [ ] Test scheduler execution in staging -6. [ ] Verify webhook delivery -7. [ ] Update API documentation (Swagger/OpenAPI) -8. [ ] Notify frontend team of: - - New `EXPIRED` status - - New `/expire` endpoint - - Blocked operations on expired escrows -9. [ ] Deploy to production -10. [ ] Monitor logs for expiration events - -## 📋 Verification Steps - -### Manual Testing -- [ ] Create escrow with deadline -- [ ] Wait for deadline to pass (or mock time) -- [ ] Call expire endpoint as depositor -- [ ] Verify status changed to EXPIRED -- [ ] Verify event logged -- [ ] Verify webhook dispatched -- [ ] Try to fulfill condition (should fail) -- [ ] Try to confirm condition (should fail) -- [ ] Try to release escrow (should fail) - -### Scheduler Testing -- [ ] Create escrow with past deadline -- [ ] Wait for hourly cron -- [ ] Verify auto-expiration -- [ ] Check notification delivery - -### Error Case Testing -- [ ] Try to expire before deadline (should fail) -- [ ] Try to expire without deadline (should fail) -- [ ] Try to expire as unauthorized user (should fail) -- [ ] Try to expire completed escrow (should fail) -- [ ] Try to expire already expired escrow (should fail) - -## 🎯 Success Criteria - -- [x] All tests pass -- [x] No TypeScript errors -- [x] State machine enforces rules -- [x] Authorization properly checked -- [x] Events properly logged -- [x] Webhooks dispatched -- [x] Operations blocked after expiry -- [x] Scheduler auto-expires overdue escrows -- [x] Documentation complete - -## 📝 Notes - -- Existing escrows without `expiresAt` are unaffected -- No database migration required -- Backward compatible -- Requires app restart to load new enum values - -## 🚀 Ready for Deployment - -All implementation tasks completed. Code is ready for review and deployment. diff --git a/apps/backend/README.md b/apps/backend/README.md deleted file mode 100644 index d671984a..00000000 --- a/apps/backend/README.md +++ /dev/null @@ -1,118 +0,0 @@ -

- Nest Logo -

- -[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 -[circleci-url]: https://circleci.com/gh/nestjs/nest - -

A progressive Node.js framework for building efficient and scalable server-side applications.

-

-NPM Version -Package License -NPM Downloads -CircleCI -Discord -Backers on Open Collective -Sponsors on Open Collective - Donate us - Support us - Follow us on Twitter -

- - -## Description - -[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. - -## Project setup - -```bash -$ npm install -``` - -## Compile and run the project - -```bash -# development -$ npm run start - -# watch mode -$ npm run start:dev - -# production mode -$ npm run start:prod -``` - -## Run tests - -```bash -# unit tests -$ npm run test - -# e2e tests -$ npm run test:e2e - -# test coverage -$ npm run test:cov -``` - -## Database Migrations - -This project uses TypeORM migrations for database schema management. - -```bash -# Generate a new migration based on entity changes -$ npm run migration:generate -- src/migrations/MigrationName - -# Execute pending migrations -$ npm run migration:run - -# Rollback the last executed migration -$ npm run migration:revert - -# List all migrations and their status -$ npm run migration:show -``` - -Note: In development, `synchronize: false` is set to ensure schema changes are always handled via migrations. Migrations run automatically on application startup (`migrationsRun: true`). - -## Deployment - -When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information. - -If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps: - -```bash -$ npm install -g @nestjs/mau -$ mau deploy -``` - -With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure. - -## Resources - -Check out a few resources that may come in handy when working with NestJS: - -- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework. -- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy). -- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/). -- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks. -- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com). -- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com). -- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs). -- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com). - -## Support - -Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). - -## Stay in touch - -- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec) -- Website - [https://nestjs.com](https://nestjs.com/) -- Twitter - [@nestframework](https://twitter.com/nestframework) - -## License - -Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE). diff --git a/apps/backend/STELLAR_SERVICE.md b/apps/backend/STELLAR_SERVICE.md deleted file mode 100644 index a85490fc..00000000 --- a/apps/backend/STELLAR_SERVICE.md +++ /dev/null @@ -1,115 +0,0 @@ -# Stellar Service Integration - -This document describes the Stellar blockchain integration implemented in the Vaultix backend. - -## Overview - -The Stellar service provides a bridge between the off-chain escrow system and the Stellar blockchain, enabling secure on-chain transactions for escrow operations. - -## Components - -### 1. Configuration (`src/config/stellar.config.ts`) - -Manages Stellar network configuration including: -- Network selection (testnet/mainnet) -- Horizon URL -- Network passphrase -- Wallet secrets -- Timeout and retry settings - -### 2. Core Service (`src/services/stellar.service.ts`) - -Provides core Stellar functionality: -- Account information retrieval -- Transaction building for escrow operations -- Transaction submission with retry logic -- Transaction status monitoring -- Key validation and generation - -### 3. Escrow Operations (`src/services/stellar/escrow-operations.ts`) - -Specialized operations for escrow functionality: -- Escrow initialization -- Funding operations -- Milestone releases -- Confirmation operations -- Cancel and completion operations - -### 4. Retry Utility (`src/utils/retry.util.ts`) - -Implements exponential backoff retry logic for network resilience. - -### 5. Module Integration (`src/modules/stellar/stellar.module.ts`) - -NestJS module that bundles Stellar services for dependency injection. - -### 6. Escrow Integration (`src/modules/escrow/services/escrow-stellar-integration.service.ts`) - -Bridges the escrow business logic with Stellar blockchain operations: -- Creating on-chain escrows -- Funding escrows -- Releasing milestone payments -- Confirming deliveries -- Canceling and completing escrows -- Monitoring on-chain state - -## Environment Variables - -Add the following to your `.env` file: - -```bash -# Stellar Configuration -STELLAR_NETWORK=testnet # testnet or mainnet -WALLET_SECRET="your-stellar-wallet-secret" # Secret key for signing transactions -STELLAR_TIMEOUT=60000 # Request timeout in ms -STELLAR_MAX_RETRIES=3 # Max retry attempts for failed requests -STELLAR_RETRY_DELAY=1000 # Base delay between retries in ms -``` - -## Usage Examples - -### Creating an On-Chain Escrow - -```typescript -// In your controller/service -const txHash = await this.escrowStellarIntegrationService.createOnChainEscrow(escrowId); -``` - -### Funding an Escrow - -```typescript -const txHash = await this.escrowStellarIntegrationService.fundOnChainEscrow( - escrowId, - funderPublicKey, - amount, - assetCode -); -``` - -### Releasing a Milestone Payment - -```typescript -const txHash = await this.escrowStellarIntegrationService.releaseMilestonePayment( - escrowId, - milestoneId, - releaserPublicKey, - recipientPublicKey, - amount, - assetCode -); -``` - -## Error Handling - -The service includes comprehensive error handling with: -- Stellar-specific error mapping -- Network failure retries -- Detailed logging -- Validation checks - -## Security Considerations - -- Private keys are handled securely via environment variables -- Transaction validation occurs before submission -- Rate limiting prevents abuse -- Proper access controls on escrow operations \ No newline at end of file diff --git a/apps/backend/build_errors.txt b/apps/backend/build_errors.txt deleted file mode 100644 index 0639c0a1..00000000 Binary files a/apps/backend/build_errors.txt and /dev/null differ diff --git a/apps/backend/build_errors_2.txt b/apps/backend/build_errors_2.txt deleted file mode 100644 index 238f4ea3..00000000 Binary files a/apps/backend/build_errors_2.txt and /dev/null differ diff --git a/apps/backend/build_output.txt b/apps/backend/build_output.txt deleted file mode 100644 index 238f4ea3..00000000 Binary files a/apps/backend/build_output.txt and /dev/null differ diff --git a/apps/backend/data/vaultix.db b/apps/backend/data/vaultix.db deleted file mode 100644 index 9a44203f..00000000 Binary files a/apps/backend/data/vaultix.db and /dev/null differ diff --git a/apps/backend/docs/AUTH_API.md b/apps/backend/docs/AUTH_API.md deleted file mode 100644 index 7f34ccf4..00000000 --- a/apps/backend/docs/AUTH_API.md +++ /dev/null @@ -1,307 +0,0 @@ -# Authentication API Documentation - -This document describes the wallet-based authentication system for Vaultix, which uses Stellar wallet signatures for passwordless authentication. - -## Overview - -The authentication system follows a challenge-response pattern: - -1. **Challenge Generation**: Client requests a nonce for their wallet address -2. **Signature Verification**: Client signs the challenge message with their private key -3. **Token Issuance**: Server verifies signature and issues JWT tokens -4. **Session Management**: Client uses access token for API calls, refresh token for renewal - -## Base URL - -``` -http://localhost:3000/auth -``` - -## Endpoints - -### 1. Generate Challenge - -**POST** `/auth/challenge` - -Generates a unique nonce (challenge) for the given wallet address. - -#### Request Body - -```json -{ - "walletAddress": "GD5DJQDZYKGHIHYLF4IR5J6DZLZBW5QQHXK5RWSLTZ5FT5ZJPQK5LW5D" -} -``` - -#### Response - -```json -{ - "nonce": "a1b2c3d4e5f6789012345678901234ab", - "message": "Sign this message to authenticate with Vaultix: a1b2c3d4e5f6789012345678901234ab" -} -``` - -#### Status Codes - -- `200 OK`: Challenge generated successfully -- `400 Bad Request`: Invalid wallet address format -- `429 Too Many Requests`: Rate limit exceeded - ---- - -### 2. Verify Signature - -**POST** `/auth/verify` - -Verifies the wallet signature and returns authentication tokens. - -#### Request Body - -```json -{ - "walletAddress": "GD5DJQDZYKGHIHYLF4IR5J6DZLZBW5QQHXK5RWSLTZ5FT5ZJPQK5LW5D", - "signature": "4a1b2c3d4e5f6789012345678901234ab...", - "publicKey": "GD5DJQDZYKGHIHYLF4IR5J6DZLZBW5QQHXK5RWSLTZ5FT5ZJPQK5LW5D" -} -``` - -#### Response - -```json -{ - "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", - "refreshToken": "a1b2c3d4e5f6789012345678901234ab..." -} -``` - -#### Status Codes - -- `200 OK`: Authentication successful -- `401 Unauthorized`: Invalid signature or challenge -- `400 Bad Request`: Missing required fields - ---- - -### 3. Refresh Access Token - -**POST** `/auth/refresh` - -Exchanges a refresh token for a new access token. - -#### Request Body - -```json -{ - "refreshToken": "a1b2c3d4e5f6789012345678901234ab..." -} -``` - -#### Response - -```json -{ - "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", - "refreshToken": "b2c3d4e5f6789012345678901234abcd..." -} -``` - -#### Status Codes - -- `200 OK`: Token refreshed successfully -- `401 Unauthorized`: Invalid or expired refresh token - ---- - -### 4. Get Current User - -**GET** `/auth/me` - -Returns information about the currently authenticated user. - -#### Headers - -``` -Authorization: Bearer -``` - -#### Response - -```json -{ - "id": "550e8400-e29b-41d4-a716-446655440000", - "walletAddress": "GD5DJQDZYKGHIHYLF4IR5J6DZLZBW5QQHXK5RWSLTZ5FT5ZJPQK5LW5D", - "isActive": true, - "createdAt": "2024-01-22T10:30:00.000Z" -} -``` - -#### Status Codes - -- `200 OK`: User information retrieved -- `401 Unauthorized`: Invalid or missing access token - ---- - -### 5. Logout - -**POST** `/auth/logout` - -Invalidates the refresh token and logs out the user. - -#### Headers - -``` -Authorization: Bearer -``` - -#### Request Body - -```json -{ - "refreshToken": "a1b2c3d4e5f6789012345678901234ab..." -} -``` - -#### Response - -```json -{ - "message": "Successfully logged out" -} -``` - -#### Status Codes - -- `200 OK`: Logout successful -- `401 Unauthorized`: Invalid access token - -## Security Considerations - -### Rate Limiting - -All authentication endpoints are rate-limited to prevent abuse: -- **10 requests per minute** per IP address -- Exceeding limits results in `429 Too Many Requests` responses - -### Token Security - -#### Access Tokens -- **Expiration**: 15 minutes -- **Usage**: API authentication -- **Format**: JWT with user ID and wallet address - -#### Refresh Tokens -- **Expiration**: 7 days -- **Usage**: Token renewal -- **Storage**: Server-side with user association -- **Invalidation**: Automatic on logout or reuse - -### Signature Verification - -The system uses Stellar SDK for cryptographic verification: -- Messages are signed using the wallet's private key -- Signatures are verified against the provided public key -- Public key must match the wallet address - -### Best Practices - -1. **Token Storage**: Store tokens securely on the client side -2. **HTTPS**: Always use HTTPS in production -3. **Token Rotation**: Use refresh tokens to maintain sessions -4. **Error Handling**: Implement proper error handling for authentication failures -5. **Nonce Usage**: Each nonce is single-use and expires after verification - -## Error Responses - -All endpoints may return these common error responses: - -### 400 Bad Request -```json -{ - "message": "Validation failed", - "error": "Bad Request" -} -``` - -### 401 Unauthorized -```json -{ - "message": "Invalid or expired token", - "error": "Unauthorized" -} -``` - -### 429 Too Many Requests -```json -{ - "message": "Too many requests", - "error": "Too Many Requests" -} -``` - -## Integration Example - -### JavaScript/TypeScript Client - -```typescript -import * as StellarSdk from 'stellar-sdk'; - -class VaultixAuth { - private baseURL = 'http://localhost:3000/auth'; - - async authenticate(walletKeypair: StellarSdk.Keypair) { - // 1. Get challenge - const challengeResponse = await fetch(`${this.baseURL}/challenge`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - walletAddress: walletKeypair.publicKey() - }) - }); - const { message } = await challengeResponse.json(); - - // 2. Sign message - const signature = walletKeypair.sign(message).toString('hex'); - - // 3. Verify and get tokens - const verifyResponse = await fetch(`${this.baseURL}/verify`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - walletAddress: walletKeypair.publicKey(), - signature, - publicKey: walletKeypair.publicKey() - }) - }); - - return await verifyResponse.json(); - } - - async getCurrentUser(accessToken: string) { - const response = await fetch(`${this.baseURL}/me`, { - headers: { - 'Authorization': `Bearer ${accessToken}`, - 'Content-Type': 'application/json' - } - }); - - return await response.json(); - } -} -``` - -## Testing - -The authentication system includes comprehensive e2e tests covering: -- Challenge generation and uniqueness -- Signature verification -- Token issuance and validation -- Refresh token functionality -- Rate limiting behavior -- Error scenarios - -Run tests with: -```bash -npm run test:e2e -``` diff --git a/apps/backend/docs/DEADLINE_ENFORCEMENT.md b/apps/backend/docs/DEADLINE_ENFORCEMENT.md deleted file mode 100644 index 185782e9..00000000 --- a/apps/backend/docs/DEADLINE_ENFORCEMENT.md +++ /dev/null @@ -1,149 +0,0 @@ -# Escrow Deadline Enforcement - -## Overview - -This document describes the deadline enforcement mechanism for escrows, ensuring that escrows cannot remain in limbo indefinitely. - -## Lifecycle States - -Escrows follow this state machine: - -``` -PENDING → ACTIVE → COMPLETED - ↓ ↓ - EXPIRED EXPIRED - ↓ ↓ -CANCELLED DISPUTED → EXPIRED -``` - -### Terminal States -- `COMPLETED`: Escrow successfully released -- `CANCELLED`: Escrow cancelled before completion -- `EXPIRED`: Escrow deadline exceeded - -## Deadline Field - -Each escrow has an optional `expiresAt` field (DateTime) that defines when the escrow expires. - -## Expiration Rules - -### Before Deadline -- Normal operations proceed as usual -- Milestone releases and confirmations work normally -- All parties can interact with the escrow - -### After Deadline -- No new condition fulfillments or confirmations allowed -- No releases allowed (must use expire endpoint) -- Depositor or arbitrator can trigger expiration - -## Expiration Endpoint - -### POST `/escrows/:id/expire` - -Triggers expiration of an overdue escrow. - -**Authorization:** -- Depositor (creator) can expire -- Arbitrator can expire -- Other parties cannot expire - -**Validation:** -- Escrow must have an `expiresAt` deadline -- Current time must be past the deadline -- Escrow must not be in a terminal state - -**Effect:** -- Sets status to `EXPIRED` -- Logs `EXPIRED` event with metadata -- Dispatches `escrow.expired` webhook -- Prevents further operations - -**Request Body:** -```json -{ - "reason": "Optional reason for expiration" -} -``` - -## Automatic Expiration (Scheduler) - -A cron job runs hourly to automatically expire overdue escrows: - -### Expired PENDING Escrows -- Status changed to `EXPIRED` -- `isActive` set to false -- Event logged with reason `EXPIRED_PENDING` -- Parties notified via webhook - -### Expired ACTIVE Escrows -- Status changed to `EXPIRED` -- Event logged with reason `EXPIRED_ACTIVE` -- Parties notified with `requiresArbitration: true` -- Arbitrator can review and decide on fund distribution - -## Invariants - -1. **No operations after expiry**: Once expired, no condition fulfillments, confirmations, or releases are allowed -2. **No expiration of terminal states**: Cannot expire COMPLETED, CANCELLED, or already EXPIRED escrows -3. **Deadline required**: Cannot expire an escrow without an `expiresAt` field -4. **Time validation**: Cannot expire before deadline passes - -## Events - -### EXPIRED Event -```json -{ - "eventType": "expired", - "data": { - "reason": "Deadline exceeded", - "previousStatus": "active", - "expiresAt": "2024-01-01T00:00:00Z", - "expiredAt": "2024-01-02T10:30:00Z" - } -} -``` - -## Webhooks - -### escrow.expired -Dispatched when an escrow is expired (manually or automatically). - -```json -{ - "escrowId": "uuid", - "triggeredBy": "userId" -} -``` - -## Interaction with Other Features - -### Disputes -- Expired ACTIVE escrows can transition to DISPUTED if needed -- Arbitrators can review expired escrows and make decisions -- DISPUTED escrows can also expire if resolution takes too long - -### Milestones/Conditions -- Expired escrows block new condition fulfillments -- Already fulfilled conditions remain in their state -- Buyers cannot confirm conditions on expired escrows - -### Refunds -- Expiration doesn't automatically refund -- Arbitrator or depositor must initiate refund after expiration -- Refund logic depends on business rules (full refund, partial, etc.) - -## Testing - -Comprehensive tests cover: -- `test_expire_escrow_refunds_unreleased_amount`: Verifies expiration by depositor -- `test_expire_escrow_after_completion_rejected`: Cannot expire completed escrows -- `test_expire_escrow_before_deadline_rejected`: Cannot expire before deadline -- `test_expire_by_arbitrator`: Arbitrator can trigger expiration -- `test_expire_without_deadline`: Rejects escrows without deadline -- `test_expire_already_expired`: Idempotent expiration handling -- State machine tests for all EXPIRED transitions - -## Migration Notes - -Existing escrows without `expiresAt` are not affected by expiration logic. They can continue indefinitely unless manually cancelled or completed. diff --git a/apps/backend/docs/EXPIRATION_EXAMPLE.md b/apps/backend/docs/EXPIRATION_EXAMPLE.md deleted file mode 100644 index 9c68e5a7..00000000 --- a/apps/backend/docs/EXPIRATION_EXAMPLE.md +++ /dev/null @@ -1,155 +0,0 @@ -# Escrow Expiration Usage Examples - -## Creating an Escrow with Deadline - -```typescript -POST /escrows -{ - "title": "Freelance Project Payment", - "amount": 1000, - "asset": "XLM", - "expiresAt": "2024-12-31T23:59:59Z", - "parties": [ - { "userId": "seller-123", "role": "seller" }, - { "userId": "arbitrator-456", "role": "arbitrator" } - ], - "conditions": [ - { "description": "Project delivered", "type": "manual" } - ] -} -``` - -## Scenario 1: Normal Completion Before Deadline - -1. Seller fulfills condition -2. Buyer confirms condition -3. Escrow auto-releases -4. Status: `COMPLETED` - -## Scenario 2: Deadline Exceeded - Depositor Triggers Expiration - -```typescript -// Current time: 2025-01-05 (past deadline) -POST /escrows/escrow-123/expire -Authorization: Bearer -{ - "reason": "Seller became unresponsive" -} - -// Response -{ - "id": "escrow-123", - "status": "expired", - "expiresAt": "2024-12-31T23:59:59Z", - ... -} -``` - -## Scenario 3: Automatic Expiration by Scheduler - -``` -Cron runs hourly: -- Finds escrow-456 with expiresAt: 2024-12-31 -- Current time: 2025-01-01 02:00 -- Automatically sets status to EXPIRED -- Sends notifications to all parties -``` - -## Scenario 4: Arbitrator Intervention - -```typescript -// Escrow expired while ACTIVE -// Arbitrator reviews and decides - -POST /escrows/escrow-789/expire -Authorization: Bearer -{ - "reason": "Deadline exceeded, reviewing for resolution" -} - -// Arbitrator can then: -// - Issue refund to depositor -// - Release funds to seller if work was done -// - Split funds based on partial completion -``` - -## Scenario 5: Attempting Operations After Expiration - -```typescript -// Trying to fulfill condition after expiration -POST /escrows/escrow-123/conditions/cond-1/fulfill -{ - "notes": "Work completed" -} - -// Response: 400 Bad Request -{ - "statusCode": 400, - "message": "Cannot fulfill conditions on an expired escrow" -} -``` - -## Scenario 6: Checking Expiration Status - -```typescript -GET /escrows/escrow-123 - -// Response -{ - "id": "escrow-123", - "status": "expired", - "expiresAt": "2024-12-31T23:59:59Z", - "events": [ - { - "eventType": "expired", - "data": { - "reason": "EXPIRED_ACTIVE", - "expiredAt": "2025-01-01T02:00:00Z" - } - } - ] -} -``` - -## Error Cases - -### Expire Before Deadline -```typescript -POST /escrows/escrow-123/expire - -// Response: 400 Bad Request -{ - "message": "Escrow has not expired yet. Expires at: 2024-12-31T23:59:59Z" -} -``` - -### Expire Without Deadline -```typescript -POST /escrows/escrow-456/expire - -// Response: 400 Bad Request -{ - "message": "Escrow has no expiration deadline" -} -``` - -### Unauthorized Expiration -```typescript -POST /escrows/escrow-123/expire -Authorization: Bearer - -// Response: 403 Forbidden -{ - "message": "Only the depositor or arbitrator can expire an escrow" -} -``` - -### Expire Completed Escrow -```typescript -POST /escrows/escrow-789/expire - -// Response: 400 Bad Request -{ - "message": "Cannot expire an escrow that is already completed" -} -``` diff --git a/apps/backend/docs/QUICK_REFERENCE_EXPIRATION.md b/apps/backend/docs/QUICK_REFERENCE_EXPIRATION.md deleted file mode 100644 index 39a40856..00000000 --- a/apps/backend/docs/QUICK_REFERENCE_EXPIRATION.md +++ /dev/null @@ -1,83 +0,0 @@ -# Quick Reference: Escrow Expiration - -## Status Flow -``` -PENDING/ACTIVE/DISPUTED → EXPIRED (terminal) -``` - -## API Endpoint -``` -POST /escrows/:id/expire -Body: { "reason": "optional" } -Auth: Depositor or Arbitrator only -``` - -## Validation Rules -- ✅ Must have `expiresAt` deadline -- ✅ Current time > `expiresAt` -- ✅ Not in terminal state (COMPLETED, CANCELLED, EXPIRED) -- ✅ User is depositor or arbitrator - -## Automatic Expiration -- Runs: Every hour (cron) -- PENDING → EXPIRED -- ACTIVE → EXPIRED - -## Blocked Operations After Expiry -- ❌ Fulfill conditions -- ❌ Confirm conditions -- ❌ Release escrow -- ✅ View escrow details -- ✅ View events - -## Event Emitted -```json -{ - "eventType": "expired", - "data": { - "reason": "string", - "previousStatus": "active|pending|disputed", - "expiresAt": "ISO8601", - "expiredAt": "ISO8601" - } -} -``` - -## Webhook -``` -escrow.expired -{ "escrowId": "uuid", "triggeredBy": "userId" } -``` - -## Error Messages -- "Cannot expire an escrow that is already {status}" -- "Escrow has no expiration deadline" -- "Escrow has not expired yet. Expires at: {date}" -- "Only the depositor or arbitrator can expire an escrow" -- "Cannot {operation} on an expired escrow" - -## Code Examples - -### Check if Expired -```typescript -if (escrow.status === EscrowStatus.EXPIRED) { - // Handle expired state -} -``` - -### Expire Escrow -```typescript -await escrowService.expire( - escrowId, - { reason: 'Deadline exceeded' }, - userId, - ipAddress -); -``` - -### Guard Against Expiry -```typescript -if (escrow.expiresAt && escrow.expiresAt < new Date()) { - throw new BadRequestException('Cannot operate on expired escrow'); -} -``` diff --git a/apps/backend/eslint.config.mjs b/apps/backend/eslint.config.mjs deleted file mode 100644 index 06618050..00000000 --- a/apps/backend/eslint.config.mjs +++ /dev/null @@ -1,145 +0,0 @@ -// @ts-check -import eslint from '@eslint/js'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import globals from 'globals'; -import tseslint from 'typescript-eslint'; - -export default tseslint.config( - { - ignores: ['eslint.config.mjs'], - }, - eslint.configs.recommended, - ...tseslint.configs.recommendedTypeChecked, - eslintPluginPrettierRecommended, - { - languageOptions: { - globals: { - ...globals.node, - ...globals.jest, - }, - sourceType: 'commonjs', - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, - { - files: ['**/*.spec.ts', '**/*.e2e-spec.ts', 'test/setup/test-app.factory.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/unbound-method': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unused-vars': 'off', - }, - }, - { - files: ['src/modules/admin/admin.service.ts'], - rules: { - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-argument': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - }, - }, - { - files: ['src/modules/auth/middleware/admin.guard.ts'], - rules: { - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-enum-comparison': 'off', - }, - }, - { - files: ['src/modules/escrow/services/escrow-scheduler.service.ts'], - rules: { - '@typescript-eslint/require-await': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-explicit-any': 'off', - }, - }, - { - files: ['src/scripts/admin-seed.ts'], - rules: { - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/no-floating-promises': 'off', - }, - }, - { - files: ['src/modules/escrow/controllers/escrow-scheduler.controller.ts'], - rules: { - '@typescript-eslint/no-unused-vars': 'off', - }, - }, - { - files: ['src/modules/escrow/dto/create-escrow.dto.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, - { - files: ['src/modules/escrow/entities/condition.entity.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, - { - files: ['src/modules/escrow/entities/escrow-event.entity.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, - { - files: ['src/modules/escrow/services/escrow-stellar-integration.service.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, - { - files: ['src/modules/escrow/services/escrow.service.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, - { - files: ['src/services/stellar.service.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, - { - files: ['src/services/stellar/escrow-operations.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, - { - files: ['src/types/stellar.types.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - }, - }, - { - files: ['src/utils/token.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - }, - }, - { - files: ['src/clients/client.ts'], - rules: { - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - }, - }, -); diff --git a/apps/backend/lint_my_files.txt b/apps/backend/lint_my_files.txt deleted file mode 100644 index f2772ec9..00000000 Binary files a/apps/backend/lint_my_files.txt and /dev/null differ diff --git a/apps/backend/lint_output.txt b/apps/backend/lint_output.txt deleted file mode 100644 index 8fdc19cd..00000000 Binary files a/apps/backend/lint_output.txt and /dev/null differ diff --git a/apps/backend/nest-cli.json b/apps/backend/nest-cli.json deleted file mode 100644 index f9aa683b..00000000 --- a/apps/backend/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/apps/backend/package-lock.json b/apps/backend/package-lock.json deleted file mode 100644 index 364ff71e..00000000 --- a/apps/backend/package-lock.json +++ /dev/null @@ -1,12667 +0,0 @@ -{ - "name": "backend", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "backend", - "version": "0.0.1", - "license": "UNLICENSED", - "dependencies": { - "@nestjs/common": "^11.1.14", - "@nestjs/config": "^4.0.3", - "@nestjs/core": "^11.0.1", - "@nestjs/jwt": "^11.0.2", - "@nestjs/mapped-types": "^2.1.0", - "@nestjs/passport": "^11.0.5", - "@nestjs/platform-express": "^11.0.1", - "@nestjs/platform-socket.io": "^11.0.1", - "@nestjs/schedule": "^6.1.1", - "@nestjs/swagger": "^11.2.0", - "@nestjs/terminus": "^11.0.0", - "@nestjs/throttler": "^6.5.0", - "@nestjs/typeorm": "^11.0.0", - "@nestjs/websockets": "^11.0.1", - "@stellar/stellar-sdk": "^14.5.0", - "@types/bcrypt": "^6.0.0", - "@types/multer": "^1.4.12", - "@types/passport-jwt": "^4.0.1", - "axios": "^1.13.5", - "bcrypt": "^6.0.0", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.3", - "dotenv": "^17.3.1", - "multer": "^1.4.5-lts.1", - "nodemailer": "^8.0.3", - "passport": "^0.7.0", - "passport-jwt": "^4.0.1", - "reflect-metadata": "^0.2.2", - "rxjs": "^7.8.1", - "socket.io": "^4.8.3", - "sqlite3": "^5.1.7", - "stellar-sdk": "^13.3.0", - "swagger-ui-express": "^5.0.1", - "typeorm": "^0.3.28" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "^9.18.0", - "@nestjs/cli": "^11.0.0", - "@nestjs/schematics": "^11.0.0", - "@nestjs/testing": "^11.0.1", - "@types/express": "^5.0.0", - "@types/jest": "^29.5.14", - "@types/node": "^22.19.11", - "@types/nodemailer": "^7.0.11", - "@types/supertest": "^6.0.2", - "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.1", - "eslint-plugin-prettier": "^5.2.2", - "globals": "^16.0.0", - "jest": "^29.7.0", - "prettier": "^3.4.2", - "source-map-support": "^0.5.21", - "supertest": "^7.0.0", - "ts-jest": "^29.4.11", - "ts-loader": "^9.5.2", - "ts-node": "^10.9.2", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.7.3", - "typescript-eslint": "^8.20.0" - } - }, - "node_modules/@angular-devkit/core": { - "version": "19.2.19", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", - "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/core/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@angular-devkit/core/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@angular-devkit/core/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@angular-devkit/schematics": { - "version": "19.2.19", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.19.tgz", - "integrity": "sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.19", - "jsonc-parser": "3.3.1", - "magic-string": "0.30.17", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics-cli": { - "version": "19.2.19", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-19.2.19.tgz", - "integrity": "sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.19", - "@angular-devkit/schematics": "19.2.19", - "@inquirer/prompts": "7.3.2", - "ansi-colors": "4.1.3", - "symbol-observable": "4.0.0", - "yargs-parser": "21.1.1" - }, - "bin": { - "schematics": "bin/schematics.js" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/@inquirer/prompts": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", - "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^4.1.2", - "@inquirer/confirm": "^5.1.6", - "@inquirer/editor": "^4.2.7", - "@inquirer/expand": "^4.0.9", - "@inquirer/input": "^4.1.6", - "@inquirer/number": "^3.0.9", - "@inquirer/password": "^4.0.9", - "@inquirer/rawlist": "^4.0.9", - "@inquirer/search": "^3.0.9", - "@inquirer/select": "^4.0.9" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/schematics/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", - "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", - "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.7" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", - "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", - "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", - "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", - "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@borewit/text-codec": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.1.tgz", - "integrity": "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", - "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "license": "MIT", - "optional": true - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@inquirer/ansi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", - "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/checkbox": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", - "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/confirm": { - "version": "5.1.21", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", - "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/core": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", - "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/editor": { - "version": "4.2.23", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", - "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/external-editor": "^1.0.3", - "@inquirer/type": "^3.0.10" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/expand": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", - "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/figures": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", - "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@inquirer/input": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", - "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/number": { - "version": "3.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", - "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/password": { - "version": "4.0.23", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", - "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/prompts": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", - "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/checkbox": "^4.3.2", - "@inquirer/confirm": "^5.1.21", - "@inquirer/editor": "^4.2.23", - "@inquirer/expand": "^4.0.23", - "@inquirer/input": "^4.3.1", - "@inquirer/number": "^3.0.23", - "@inquirer/password": "^4.0.23", - "@inquirer/rawlist": "^4.1.11", - "@inquirer/search": "^3.2.2", - "@inquirer/select": "^4.4.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/rawlist": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", - "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/search": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", - "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/select": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", - "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/core": "^10.3.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", - "yoctocolors-cjs": "^2.1.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@inquirer/type": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", - "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", - "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@lukeed/csprng": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", - "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@microsoft/tsdoc": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", - "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", - "license": "MIT" - }, - "node_modules/@nestjs/cli": { - "version": "11.0.16", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-11.0.16.tgz", - "integrity": "sha512-P0H+Vcjki6P5160E5QnMt3Q0X5FTg4PZkP99Ig4lm/4JWqfw32j3EXv3YBTJ2DmxLwOQ/IS9F7dzKpMAgzKTGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.19", - "@angular-devkit/schematics": "19.2.19", - "@angular-devkit/schematics-cli": "19.2.19", - "@inquirer/prompts": "7.10.1", - "@nestjs/schematics": "^11.0.1", - "ansis": "4.2.0", - "chokidar": "4.0.3", - "cli-table3": "0.6.5", - "commander": "4.1.1", - "fork-ts-checker-webpack-plugin": "9.1.0", - "glob": "13.0.0", - "node-emoji": "1.11.0", - "ora": "5.4.1", - "tsconfig-paths": "4.2.0", - "tsconfig-paths-webpack-plugin": "4.2.0", - "typescript": "5.9.3", - "webpack": "5.104.1", - "webpack-node-externals": "3.0.0" - }, - "bin": { - "nest": "bin/nest.js" - }, - "engines": { - "node": ">= 20.11" - }, - "peerDependencies": { - "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0", - "@swc/core": "^1.3.62" - }, - "peerDependenciesMeta": { - "@swc/cli": { - "optional": true - }, - "@swc/core": { - "optional": true - } - } - }, - "node_modules/@nestjs/common": { - "version": "11.1.14", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-11.1.14.tgz", - "integrity": "sha512-IN/tlqd7Nl9gl6f0jsWEuOrQDaCI9vHzxv0fisHysfBQzfQIkqlv5A7w4Qge02BUQyczXT9HHPgHtWHCxhjRng==", - "license": "MIT", - "dependencies": { - "file-type": "21.3.0", - "iterare": "1.2.1", - "load-esm": "1.0.3", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "class-transformer": ">=0.4.1", - "class-validator": ">=0.13.2", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } - }, - "node_modules/@nestjs/config": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.3.tgz", - "integrity": "sha512-FQ3M3Ohqfl+nHAn5tp7++wUQw0f2nAk+SFKe8EpNRnIifPqvfJP6JQxPKtFLMOHbyer4X646prFG4zSRYEssQQ==", - "license": "MIT", - "dependencies": { - "dotenv": "17.2.3", - "dotenv-expand": "12.0.3", - "lodash": "4.17.23" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "rxjs": "^7.1.0" - } - }, - "node_modules/@nestjs/config/node_modules/dotenv": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", - "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/@nestjs/core": { - "version": "11.1.12", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-11.1.12.tgz", - "integrity": "sha512-97DzTYMf5RtGAVvX1cjwpKRiCUpkeQ9CCzSAenqkAhOmNVVFaApbhuw+xrDt13rsCa2hHVOYPrV4dBgOYMJjsA==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@nuxt/opencollective": "0.4.1", - "fast-safe-stringify": "2.1.1", - "iterare": "1.2.1", - "path-to-regexp": "8.3.0", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "engines": { - "node": ">= 20" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/microservices": "^11.0.0", - "@nestjs/platform-express": "^11.0.0", - "@nestjs/websockets": "^11.0.0", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/platform-express": { - "optional": true - }, - "@nestjs/websockets": { - "optional": true - } - } - }, - "node_modules/@nestjs/jwt": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-11.0.2.tgz", - "integrity": "sha512-rK8aE/3/Ma45gAWfCksAXUNbOoSOUudU0Kn3rT39htPF7wsYXtKfjALKeKKJbFrIWbLjsbqfXX5bIJNvgBugGA==", - "license": "MIT", - "dependencies": { - "@types/jsonwebtoken": "9.0.10", - "jsonwebtoken": "9.0.3" - }, - "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0" - } - }, - "node_modules/@nestjs/mapped-types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz", - "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==", - "license": "MIT", - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "class-transformer": "^0.4.0 || ^0.5.0", - "class-validator": "^0.13.0 || ^0.14.0", - "reflect-metadata": "^0.1.12 || ^0.2.0" - }, - "peerDependenciesMeta": { - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } - }, - "node_modules/@nestjs/passport": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-11.0.5.tgz", - "integrity": "sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==", - "license": "MIT", - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "passport": "^0.5.0 || ^0.6.0 || ^0.7.0" - } - }, - "node_modules/@nestjs/platform-express": { - "version": "11.1.12", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-11.1.12.tgz", - "integrity": "sha512-GYK/vHI0SGz5m8mxr7v3Urx8b9t78Cf/dj5aJMZlGd9/1D9OI1hAl00BaphjEXINUJ/BQLxIlF2zUjrYsd6enQ==", - "license": "MIT", - "dependencies": { - "cors": "2.8.5", - "express": "5.2.1", - "multer": "2.0.2", - "path-to-regexp": "8.3.0", - "tslib": "2.8.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/core": "^11.0.0" - } - }, - "node_modules/@nestjs/platform-express/node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-express/node_modules/multer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", - "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.6.0", - "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/@nestjs/platform-express/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@nestjs/platform-socket.io": { - "version": "11.1.19", - "resolved": "https://registry.npmjs.org/@nestjs/platform-socket.io/-/platform-socket.io-11.1.19.tgz", - "integrity": "sha512-gu1nPIEaP5Qjjg/Cl8wXyvwGpdZGzgbtK4KcH65YRAA+GTKUkIHb4BNpLJ27Ymq/wqLJKNEbCjajfzD0BEjMGA==", - "license": "MIT", - "dependencies": { - "socket.io": "4.8.3", - "tslib": "2.8.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/websockets": "^11.0.0", - "rxjs": "^7.1.0" - } - }, - "node_modules/@nestjs/schedule": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.1.1.tgz", - "integrity": "sha512-kQl1RRgi02GJ0uaUGCrXHCcwISsCsJDciCKe38ykJZgnAeeoeVWs8luWtBo4AqAAXm4nS5K8RlV0smHUJ4+2FA==", - "license": "MIT", - "dependencies": { - "cron": "4.4.0" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "@nestjs/core": "^10.0.0 || ^11.0.0" - } - }, - "node_modules/@nestjs/schematics": { - "version": "11.0.9", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz", - "integrity": "sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.17", - "@angular-devkit/schematics": "19.2.17", - "comment-json": "4.4.1", - "jsonc-parser": "3.3.1", - "pluralize": "8.0.0" - }, - "peerDependencies": { - "typescript": ">=4.8.2" - } - }, - "node_modules/@nestjs/schematics/node_modules/@angular-devkit/core": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.17.tgz", - "integrity": "sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@nestjs/schematics/node_modules/@angular-devkit/schematics": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.17.tgz", - "integrity": "sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.17", - "jsonc-parser": "3.3.1", - "magic-string": "0.30.17", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@nestjs/schematics/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@nestjs/schematics/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nestjs/schematics/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@nestjs/swagger": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.2.6.tgz", - "integrity": "sha512-oiXOxMQqDFyv1AKAqFzSo6JPvMEs4uA36Eyz/s2aloZLxUjcLfUMELSLSNQunr61xCPTpwEOShfmO7NIufKXdA==", - "license": "MIT", - "dependencies": { - "@microsoft/tsdoc": "0.16.0", - "@nestjs/mapped-types": "2.1.0", - "js-yaml": "4.1.1", - "lodash": "4.17.23", - "path-to-regexp": "8.3.0", - "swagger-ui-dist": "5.31.0" - }, - "peerDependencies": { - "@fastify/static": "^8.0.0 || ^9.0.0", - "@nestjs/common": "^11.0.1", - "@nestjs/core": "^11.0.1", - "class-transformer": "*", - "class-validator": "*", - "reflect-metadata": "^0.1.12 || ^0.2.0" - }, - "peerDependenciesMeta": { - "@fastify/static": { - "optional": true - }, - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } - }, - "node_modules/@nestjs/terminus": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@nestjs/terminus/-/terminus-11.1.1.tgz", - "integrity": "sha512-Ssql79H+EQY/Wg108eJqN4NiNsO/tLrj+qbzOWSQUf2JE4vJQ2RG3WTqUOrYjfjWmVHD3+Ys0+azed7LSMKScw==", - "license": "MIT", - "dependencies": { - "boxen": "5.1.2", - "check-disk-space": "3.4.0" - }, - "peerDependencies": { - "@grpc/grpc-js": "*", - "@grpc/proto-loader": "*", - "@mikro-orm/core": "*", - "@mikro-orm/nestjs": "*", - "@nestjs/axios": "^2.0.0 || ^3.0.0 || ^4.0.0", - "@nestjs/common": "^10.0.0 || ^11.0.0", - "@nestjs/core": "^10.0.0 || ^11.0.0", - "@nestjs/microservices": "^10.0.0 || ^11.0.0", - "@nestjs/mongoose": "^11.0.0", - "@nestjs/sequelize": "^10.0.0 || ^11.0.0", - "@nestjs/typeorm": "^10.0.0 || ^11.0.0", - "@prisma/client": "*", - "mongoose": "*", - "reflect-metadata": "0.1.x || 0.2.x", - "rxjs": "7.x", - "sequelize": "*", - "typeorm": "*" - }, - "peerDependenciesMeta": { - "@grpc/grpc-js": { - "optional": true - }, - "@grpc/proto-loader": { - "optional": true - }, - "@mikro-orm/core": { - "optional": true - }, - "@mikro-orm/nestjs": { - "optional": true - }, - "@nestjs/axios": { - "optional": true - }, - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/mongoose": { - "optional": true - }, - "@nestjs/sequelize": { - "optional": true - }, - "@nestjs/typeorm": { - "optional": true - }, - "@prisma/client": { - "optional": true - }, - "mongoose": { - "optional": true - }, - "sequelize": { - "optional": true - }, - "typeorm": { - "optional": true - } - } - }, - "node_modules/@nestjs/testing": { - "version": "11.1.12", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.1.12.tgz", - "integrity": "sha512-W0M/i5nb9qRQpTQfJm+1mGT/+y4YezwwdcD7mxFG8JEZ5fz/ZEAk1Ayri2VBJKJUdo20B1ggnvqew4dlTMrSNg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/core": "^11.0.0", - "@nestjs/microservices": "^11.0.0", - "@nestjs/platform-express": "^11.0.0" - }, - "peerDependenciesMeta": { - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/platform-express": { - "optional": true - } - } - }, - "node_modules/@nestjs/throttler": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-6.5.0.tgz", - "integrity": "sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==", - "license": "MIT", - "peerDependencies": { - "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "reflect-metadata": "^0.1.13 || ^0.2.0" - } - }, - "node_modules/@nestjs/typeorm": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@nestjs/typeorm/-/typeorm-11.0.0.tgz", - "integrity": "sha512-SOeUQl70Lb2OfhGkvnh4KXWlsd+zA08RuuQgT7kKbzivngxzSo1Oc7Usu5VxCxACQC9wc2l9esOHILSJeK7rJA==", - "license": "MIT", - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "@nestjs/core": "^10.0.0 || ^11.0.0", - "reflect-metadata": "^0.1.13 || ^0.2.0", - "rxjs": "^7.2.0", - "typeorm": "^0.3.0" - } - }, - "node_modules/@nestjs/websockets": { - "version": "11.1.19", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.19.tgz", - "integrity": "sha512-2qo8jtIwwwgkqAI1BtnJ02EaFLrRkKA39eYXS8IhZCHilhBHCWdjnJ5cLcFq4oF+s+KZ7LcLGD/3stxJy8ijzg==", - "license": "MIT", - "dependencies": { - "iterare": "1.2.1", - "object-hash": "3.0.0", - "tslib": "2.8.1" - }, - "peerDependencies": { - "@nestjs/common": "^11.0.0", - "@nestjs/core": "^11.0.0", - "@nestjs/platform-socket.io": "^11.0.0", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "@nestjs/platform-socket.io": { - "optional": true - } - } - }, - "node_modules/@noble/curves": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", - "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.8.0" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "license": "MIT", - "optional": true, - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@nuxt/opencollective": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@nuxt/opencollective/-/opencollective-0.4.1.tgz", - "integrity": "sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==", - "license": "MIT", - "dependencies": { - "consola": "^3.2.3" - }, - "bin": { - "opencollective": "bin/opencollective.js" - }, - "engines": { - "node": "^14.18.0 || >=16.10.0", - "npm": ">=5.10.0" - } - }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", - "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "^1.1.5" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@scarf/scarf": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz", - "integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==", - "hasInstallScript": true, - "license": "Apache-2.0" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "license": "MIT" - }, - "node_modules/@sqltools/formatter": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", - "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==", - "license": "MIT" - }, - "node_modules/@stellar/js-xdr": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@stellar/js-xdr/-/js-xdr-3.1.2.tgz", - "integrity": "sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==", - "license": "Apache-2.0" - }, - "node_modules/@stellar/stellar-base": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-13.1.0.tgz", - "integrity": "sha512-90EArG+eCCEzDGj3OJNoCtwpWDwxjv+rs/RNPhvg4bulpjN/CSRj+Ys/SalRcfM4/WRC5/qAfjzmJBAuquWhkA==", - "license": "Apache-2.0", - "dependencies": { - "@stellar/js-xdr": "^3.1.2", - "base32.js": "^0.1.0", - "bignumber.js": "^9.1.2", - "buffer": "^6.0.3", - "sha.js": "^2.3.6", - "tweetnacl": "^1.0.3" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "sodium-native": "^4.3.3" - } - }, - "node_modules/@stellar/stellar-base/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/@stellar/stellar-sdk": { - "version": "14.5.0", - "resolved": "https://registry.npmjs.org/@stellar/stellar-sdk/-/stellar-sdk-14.5.0.tgz", - "integrity": "sha512-Uzjq+An/hUA+Q5ERAYPtT0+MMiwWnYYWMwozmZMjxjdL2MmSjucBDF8Q04db6K/ekU4B5cHuOfsdlrfaxQYblw==", - "license": "Apache-2.0", - "dependencies": { - "@stellar/stellar-base": "^14.0.4", - "axios": "^1.13.3", - "bignumber.js": "^9.3.1", - "commander": "^14.0.2", - "eventsource": "^2.0.2", - "feaxios": "^0.0.23", - "randombytes": "^2.1.0", - "toml": "^3.0.0", - "urijs": "^1.19.1" - }, - "bin": { - "stellar-js": "bin/stellar-js" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@stellar/stellar-sdk/node_modules/@stellar/stellar-base": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/@stellar/stellar-base/-/stellar-base-14.0.4.tgz", - "integrity": "sha512-UbNW6zbdOBXJwLAV2mMak0bIC9nw3IZVlQXkv2w2dk1jgCbJjy3oRVC943zeGE5JAm0Z9PHxrIjmkpGhayY7kw==", - "license": "Apache-2.0", - "dependencies": { - "@noble/curves": "^1.9.6", - "@stellar/js-xdr": "^3.1.2", - "base32.js": "^0.1.0", - "bignumber.js": "^9.3.1", - "buffer": "^6.0.3", - "sha.js": "^2.4.12" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@stellar/stellar-sdk/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/@stellar/stellar-sdk/node_modules/commander": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/@tokenizer/inflate": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", - "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "token-types": "^6.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "license": "MIT" - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/bcrypt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", - "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cookiejar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", - "license": "MIT", - "dependencies": { - "@types/ms": "*", - "@types/node": "*" - } - }, - "node_modules/@types/luxon": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz", - "integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==", - "license": "MIT" - }, - "node_modules/@types/methods": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", - "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/multer": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", - "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/node": { - "version": "22.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", - "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/nodemailer": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.11.tgz", - "integrity": "sha512-E+U4RzR2dKrx+u3N4DlsmLaDC6mMZOM/TPROxA0UAPiTgI0y4CEFBmZE+coGWTjakDriRsXG368lNk1u9Q0a2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/passport": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", - "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/passport-jwt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", - "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", - "license": "MIT", - "dependencies": { - "@types/jsonwebtoken": "*", - "@types/passport-strategy": "*" - } - }, - "node_modules/@types/passport-strategy": { - "version": "0.2.38", - "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", - "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", - "license": "MIT", - "dependencies": { - "@types/express": "*", - "@types/passport": "*" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/superagent": { - "version": "8.1.9", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", - "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cookiejar": "^2.1.5", - "@types/methods": "^1.1.4", - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/supertest": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.3.tgz", - "integrity": "sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/methods": "^1.1.4", - "@types/superagent": "^8.1.0" - } - }, - "node_modules/@types/validator": { - "version": "13.15.10", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", - "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz", - "integrity": "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.53.1", - "@typescript-eslint/type-utils": "8.53.1", - "@typescript-eslint/utils": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.53.1", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz", - "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.53.1", - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.1.tgz", - "integrity": "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.53.1", - "@typescript-eslint/types": "^8.53.1", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz", - "integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz", - "integrity": "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz", - "integrity": "sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1", - "@typescript-eslint/utils": "8.53.1", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz", - "integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz", - "integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.53.1", - "@typescript-eslint/tsconfig-utils": "8.53.1", - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1", - "debug": "^4.4.3", - "minimatch": "^9.0.5", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz", - "integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.53.1", - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz", - "integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.53.1", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "license": "ISC", - "optional": true - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "devOptional": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-phases": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", - "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - }, - "peerDependencies": { - "acorn": "^8.14.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "optional": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", - "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", - "license": "ISC", - "engines": { - "node": ">=14" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/app-root-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", - "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, - "node_modules/aproba": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", - "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", - "license": "ISC", - "optional": true - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axios": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", - "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.11", - "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/bare-addon-resolve": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.9.6.tgz", - "integrity": "sha512-hvOQY1zDK6u0rSr27T6QlULoVLwi8J2k8HHHJlxSfT7XQdQ/7bsS+AnjYkHtu/TkL+gm3aMXAKucJkJAbrDG/g==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-module-resolve": "^1.10.0", - "bare-semver": "^1.0.0" - }, - "peerDependencies": { - "bare-url": "*" - }, - "peerDependenciesMeta": { - "bare-url": { - "optional": true - } - } - }, - "node_modules/bare-module-resolve": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.1.tgz", - "integrity": "sha512-hbmAPyFpEq8FoZMd5sFO3u6MC5feluWoGE8YKlA8fCrl6mNtx68Wjg4DTiDJcqRJaovTvOYKfYngoBUnbaT7eg==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-semver": "^1.0.0" - }, - "peerDependencies": { - "bare-url": "*" - }, - "peerDependenciesMeta": { - "bare-url": { - "optional": true - } - } - }, - "node_modules/bare-semver": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.0.2.tgz", - "integrity": "sha512-ESVaN2nzWhcI5tf3Zzcq9aqCZ676VWzqw07eEZ0qxAcEOAFYBa0pWq8sK34OQeHLY3JsfKXZS9mDyzyxGjeLzA==", - "license": "Apache-2.0", - "optional": true - }, - "node_modules/base32.js": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.1.0.tgz", - "integrity": "sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.17", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.17.tgz", - "integrity": "sha512-agD0MgJFUP/4nvjqzIB29zRPUuCF7Ge6mEv9s8dHrtYD7QWXRcx75rOADE/d5ah1NI+0vkDl0yorDd5U852IQQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/bcrypt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", - "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^8.3.0", - "node-gyp-build": "^4.8.4" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", - "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cacache/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cacache/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cacache/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "optional": true - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001765", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz", - "integrity": "sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/chardet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", - "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/check-disk-space": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/check-disk-space/-/check-disk-space-3.4.0.tgz", - "integrity": "sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT" - }, - "node_modules/class-validator": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", - "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", - "license": "MIT", - "dependencies": { - "@types/validator": "^13.15.3", - "libphonenumber-js": "^1.11.1", - "validator": "^13.15.20" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", - "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "license": "ISC", - "optional": true, - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/comment-json": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.4.1.tgz", - "integrity": "sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "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" - } - }, - "node_modules/concat-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "license": "ISC", - "optional": true - }, - "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/cron": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/cron/-/cron-4.4.0.tgz", - "integrity": "sha512-fkdfq+b+AHI4cKdhZlppHveI/mgz2qpiYxcm+t5E5TsxX7QrLS1VE0+7GENEk9z0EeGPcpSciGv6ez24duWhwQ==", - "license": "MIT", - "dependencies": { - "@types/luxon": "~3.7.0", - "luxon": "~3.7.0" - }, - "engines": { - "node": ">=18.x" - }, - "funding": { - "type": "ko-fi", - "url": "https://ko-fi.com/intcreator" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/dayjs": { - "version": "1.11.19", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dedent": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", - "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "license": "MIT", - "optional": true - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/diff": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", - "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", - "devOptional": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dotenv": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", - "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dotenv-expand": { - "version": "12.0.3", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", - "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==", - "license": "BSD-2-Clause", - "dependencies": { - "dotenv": "^16.4.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dotenv-expand/node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "6.6.6", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.6.tgz", - "integrity": "sha512-U2SN0w3OpjFRVlrc17E6TMDmH58Xl9rai1MblNjAdwWp07Kk+llmzX0hjDpQdrDGzwmvOtgM5yI+meYX6iZ2xA==", - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "@types/ws": "^8.5.12", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.18.3" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.18.4", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", - "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "license": "MIT", - "optional": true - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-prettier": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", - "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.5.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", - "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.1", - "synckit": "^0.11.12" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/eventsource": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", - "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "license": "MIT", - "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" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/feaxios": { - "version": "0.0.23", - "resolved": "https://registry.npmjs.org/feaxios/-/feaxios-0.0.23.tgz", - "integrity": "sha512-eghR0A21fvbkcQBgZuMfQhrXxJzC0GNUGC9fXhBge33D+mFDTwl0aJ35zoQQn575BhyjQitRc5N4f+L4cP708g==", - "license": "MIT", - "dependencies": { - "is-retry-allowed": "^3.0.0" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/file-type": { - "version": "21.3.0", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz", - "integrity": "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==", - "license": "MIT", - "dependencies": { - "@tokenizer/inflate": "^0.4.1", - "strtok3": "^10.3.4", - "token-types": "^6.1.1", - "uint8array-extras": "^1.4.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", - "integrity": "sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "chalk": "^4.1.2", - "chokidar": "^4.0.1", - "cosmiconfig": "^8.2.0", - "deepmerge": "^4.2.2", - "fs-extra": "^10.0.0", - "memfs": "^3.4.1", - "minimatch": "^3.0.4", - "node-abort-controller": "^3.0.1", - "schema-utils": "^3.1.1", - "semver": "^7.3.5", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">=14.21.3" - }, - "peerDependencies": { - "typescript": ">3.6.0", - "webpack": "^5.11.0" - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/formidable": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", - "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@paralleldrive/cuid2": "^2.2.2", - "dezalgo": "^1.0.4", - "once": "^1.4.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs-minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/fs-monkey": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", - "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", - "dev": true, - "license": "Unlicense" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "devOptional": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/gauge/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC", - "optional": true - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, - "node_modules/glob": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", - "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.1.1", - "minipass": "^7.1.2", - "path-scurry": "^2.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", - "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", - "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "devOptional": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.9", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", - "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "license": "ISC", - "optional": true - }, - "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause", - "optional": true - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "optional": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "license": "ISC", - "optional": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "devOptional": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", - "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "license": "MIT", - "optional": true - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/is-retry-allowed": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-3.0.0.tgz", - "integrity": "sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/iterare": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", - "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", - "license": "ISC", - "engines": { - "node": ">=6" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonwebtoken": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", - "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", - "license": "MIT", - "dependencies": { - "jws": "^4.0.1", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", - "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", - "license": "MIT", - "dependencies": { - "jwa": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/libphonenumber-js": { - "version": "1.12.35", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.35.tgz", - "integrity": "sha512-T/Cz6iLcsZdb5jDncDcUNhSAJ0VlSC9TnsqtBNdpkaAmy24/R1RhErtNWVWBrcUZKs9hSgaVsBkc7HxYnazIfw==", - "license": "MIT" - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/load-esm": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/load-esm/-/load-esm-1.0.3.tgz", - "integrity": "sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - }, - { - "type": "buymeacoffee", - "url": "https://buymeacoffee.com/borewit" - } - ], - "license": "MIT", - "engines": { - "node": ">=13.2.0" - } - }, - "node_modules/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "license": "MIT" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/luxon": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", - "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true, - "license": "ISC" - }, - "node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "license": "ISC", - "optional": true, - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-fetch-happen/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/make-fetch-happen/node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/make-fetch-happen/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "optional": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "dev": true, - "license": "Unlicense", - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "optional": true - }, - "node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "license": "MIT", - "optional": true, - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/minipass-fetch/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-fetch/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "optional": true - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-flush/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "optional": true - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "optional": true - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "optional": true - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/multer": { - "version": "1.4.5-lts.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", - "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", - "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/multer/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/multer/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/multer/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/multer/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-abi": { - "version": "3.87.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", - "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-addon-api": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", - "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, - "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21" - } - }, - "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", - "license": "MIT", - "optional": true, - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nodemailer": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-8.0.3.tgz", - "integrity": "sha512-JQNBqvK+bj3NMhUFR3wmCl3SYcOeMotDiwDBvIoCuQdF0PvlIY0BH+FJ2CG7u4cXKPChplE78oowlH/Otsc4ZQ==", - "license": "MIT-0", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/passport": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", - "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", - "license": "MIT", - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-jwt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", - "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", - "license": "MIT", - "dependencies": { - "jsonwebtoken": "^9.0.0", - "passport-strategy": "^1.0.0" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", - "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", - "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "license": "ISC", - "optional": true - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "license": "MIT", - "optional": true, - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" - }, - "node_modules/require-addon": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", - "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "bare-addon-resolve": "^1.3.0" - }, - "engines": { - "bare": ">=1.10.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.12", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", - "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "optional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC", - "optional": true - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/sha.js": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", - "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", - "license": "(MIT AND BSD-3-Clause)", - "dependencies": { - "inherits": "^2.0.4", - "safe-buffer": "^5.2.1", - "to-buffer": "^1.2.0" - }, - "bin": { - "sha.js": "bin.js" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socket.io": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", - "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.4.1", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", - "integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==", - "license": "MIT", - "dependencies": { - "debug": "~4.4.1", - "ws": "~8.18.3" - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.6.tgz", - "integrity": "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.4.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/socket.io/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socket.io/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ip-address": "^10.0.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/sodium-native": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-4.3.3.tgz", - "integrity": "sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw==", - "license": "MIT", - "optional": true, - "dependencies": { - "require-addon": "^1.1.0" - } - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/sql-highlight": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", - "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", - "funding": [ - "https://github.com/scriptcoded/sql-highlight?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/scriptcoded" - } - ], - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/sqlite3": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", - "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1", - "tar": "^6.1.11" - }, - "optionalDependencies": { - "node-gyp": "8.x" - }, - "peerDependencies": { - "node-gyp": "8.x" - }, - "peerDependenciesMeta": { - "node-gyp": { - "optional": true - } - } - }, - "node_modules/sqlite3/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT" - }, - "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/ssri/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ssri/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "optional": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stellar-sdk": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/stellar-sdk/-/stellar-sdk-13.3.0.tgz", - "integrity": "sha512-jAA3+U7oAUueldoS4kuEhcym+DigElWq9isPxt7tjMrE7kTJ2vvY29waavUb2FSfQIWwGbuwAJTYddy2BeyJsw==", - "deprecated": "⚠️ This package has moved to @stellar/stellar-sdk! 🚚", - "license": "Apache-2.0", - "dependencies": { - "@stellar/stellar-base": "^13.1.0", - "axios": "^1.8.4", - "bignumber.js": "^9.3.0", - "eventsource": "^2.0.2", - "feaxios": "^0.0.23", - "randombytes": "^2.1.0", - "toml": "^3.0.0", - "urijs": "^1.19.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strtok3": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", - "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", - "license": "MIT", - "dependencies": { - "@tokenizer/token": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/superagent": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", - "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "component-emitter": "^1.3.1", - "cookiejar": "^2.1.4", - "debug": "^4.3.7", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.5", - "formidable": "^3.5.4", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.14.1" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/supertest": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", - "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cookie-signature": "^1.2.2", - "methods": "^1.1.2", - "superagent": "^10.3.0" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/swagger-ui-dist": { - "version": "5.31.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.31.0.tgz", - "integrity": "sha512-zSUTIck02fSga6rc0RZP3b7J7wgHXwLea8ZjgLA3Vgnb8QeOl3Wou2/j5QkzSGeoz6HusP/coYuJl33aQxQZpg==", - "license": "Apache-2.0", - "dependencies": { - "@scarf/scarf": "=1.4.0" - } - }, - "node_modules/swagger-ui-express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.1.tgz", - "integrity": "sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==", - "license": "MIT", - "dependencies": { - "swagger-ui-dist": ">=5.0.0" - }, - "engines": { - "node": ">= v0.10.32" - }, - "peerDependencies": { - "express": ">=4.0.0 || >=5.0.0-beta" - } - }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/synckit": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", - "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/terser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", - "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.16", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", - "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-buffer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", - "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", - "license": "MIT", - "dependencies": { - "isarray": "^2.0.5", - "safe-buffer": "^5.2.1", - "typed-array-buffer": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/token-types": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", - "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", - "license": "MIT", - "dependencies": { - "@borewit/text-codec": "^0.2.1", - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/toml": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", - "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==", - "license": "MIT" - }, - "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-jest": { - "version": "29.4.11", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.11.tgz", - "integrity": "sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.9", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.8.0", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <7" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ts-loader": { - "version": "9.5.4", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.4.tgz", - "integrity": "sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "typescript": "*", - "webpack": "^5.0.0" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", - "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tapable": "^2.2.1", - "tsconfig-paths": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "license": "Unlicense" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, - "node_modules/typeorm": { - "version": "0.3.28", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.28.tgz", - "integrity": "sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==", - "license": "MIT", - "dependencies": { - "@sqltools/formatter": "^1.2.5", - "ansis": "^4.2.0", - "app-root-path": "^3.1.0", - "buffer": "^6.0.3", - "dayjs": "^1.11.19", - "debug": "^4.4.3", - "dedent": "^1.7.0", - "dotenv": "^16.6.1", - "glob": "^10.5.0", - "reflect-metadata": "^0.2.2", - "sha.js": "^2.4.12", - "sql-highlight": "^6.1.0", - "tslib": "^2.8.1", - "uuid": "^11.1.0", - "yargs": "^17.7.2" - }, - "bin": { - "typeorm": "cli.js", - "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", - "typeorm-ts-node-esm": "cli-ts-node-esm.js" - }, - "engines": { - "node": ">=16.13.0" - }, - "funding": { - "url": "https://opencollective.com/typeorm" - }, - "peerDependencies": { - "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "@sap/hana-client": "^2.14.22", - "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0", - "ioredis": "^5.0.4", - "mongodb": "^5.8.0 || ^6.0.0", - "mssql": "^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0", - "mysql2": "^2.2.5 || ^3.0.1", - "oracledb": "^6.3.0", - "pg": "^8.5.1", - "pg-native": "^3.0.0", - "pg-query-stream": "^4.0.0", - "redis": "^3.1.1 || ^4.0.0 || ^5.0.14", - "sql.js": "^1.4.0", - "sqlite3": "^5.0.3", - "ts-node": "^10.7.0", - "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" - }, - "peerDependenciesMeta": { - "@google-cloud/spanner": { - "optional": true - }, - "@sap/hana-client": { - "optional": true - }, - "better-sqlite3": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "mongodb": { - "optional": true - }, - "mssql": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "oracledb": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-native": { - "optional": true - }, - "pg-query-stream": { - "optional": true - }, - "redis": { - "optional": true - }, - "sql.js": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "ts-node": { - "optional": true - }, - "typeorm-aurora-data-api-driver": { - "optional": true - } - } - }, - "node_modules/typeorm/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/typeorm/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/typeorm/node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/typeorm/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typeorm/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/typeorm/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typeorm/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.53.1.tgz", - "integrity": "sha512-gB+EVQfP5RDElh9ittfXlhZJdjSU4jUSTyE2+ia8CYyNvet4ElfaLlAIqDvQV9JPknKx0jQH1racTYe/4LaLSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.53.1", - "@typescript-eslint/parser": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1", - "@typescript-eslint/utils": "8.53.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uid": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", - "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", - "license": "MIT", - "dependencies": { - "@lukeed/csprng": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/uint8array-extras": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", - "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "license": "ISC", - "optional": true, - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/urijs": { - "version": "1.19.11", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", - "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/validator": { - "version": "13.15.26", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", - "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webpack": { - "version": "5.104.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.104.1.tgz", - "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.8", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.15.0", - "acorn-import-phases": "^1.0.3", - "browserslist": "^4.28.1", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.4", - "es-module-lexer": "^2.0.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.3", - "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.3.16", - "watchpack": "^2.4.4", - "webpack-sources": "^3.3.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-node-externals": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", - "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/webpack/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "license": "MIT", - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", - "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/apps/backend/package.json b/apps/backend/package.json deleted file mode 100644 index 70412dd8..00000000 --- a/apps/backend/package.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "name": "backend", - "version": "0.0.1", - "description": "", - "author": "", - "private": true, - "license": "UNLICENSED", - "scripts": { - "build": "nest build", - "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start", - "start:dev": "nest start --watch", - "start:debug": "nest start --debug --watch", - "start:prod": "node dist/main", - "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", - "test:watch": "jest --watch", - "test:cov": "jest --coverage", - "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json", - "seed:admin": "ts-node src/scripts/admin-seed.ts", - "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js", - "migration:generate": "npm run typeorm -- migration:generate -d src/data-source.ts", - "migration:run": "npm run typeorm -- migration:run -d src/data-source.ts", - "migration:revert": "npm run typeorm -- migration:revert -d src/data-source.ts", - "migration:show": "npm run typeorm -- migration:show -d src/data-source.ts" - }, - "dependencies": { - "@nestjs/common": "^11.1.14", - "@nestjs/config": "^4.0.3", - "@nestjs/core": "^11.0.1", - "@nestjs/jwt": "^11.0.2", - "@nestjs/mapped-types": "^2.1.0", - "@nestjs/passport": "^11.0.5", - "@nestjs/platform-express": "^11.0.1", - "@nestjs/platform-socket.io": "^11.0.1", - "@nestjs/schedule": "^6.1.1", - "@nestjs/swagger": "^11.2.0", - "@nestjs/terminus": "^11.0.0", - "@nestjs/throttler": "^6.5.0", - "@nestjs/typeorm": "^11.0.0", - "@nestjs/websockets": "^11.0.1", - "@stellar/stellar-sdk": "^14.5.0", - "@types/bcrypt": "^6.0.0", - "@types/multer": "^1.4.12", - "@types/passport-jwt": "^4.0.1", - "axios": "^1.13.5", - "bcrypt": "^6.0.0", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.3", - "dotenv": "^17.3.1", - "multer": "^1.4.5-lts.1", - "nodemailer": "^8.0.3", - "passport": "^0.7.0", - "passport-jwt": "^4.0.1", - "reflect-metadata": "^0.2.2", - "rxjs": "^7.8.1", - "socket.io": "^4.8.3", - "sqlite3": "^5.1.7", - "stellar-sdk": "^13.3.0", - "swagger-ui-express": "^5.0.1", - "typeorm": "^0.3.28" - }, - "devDependencies": { - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "^9.18.0", - "@nestjs/cli": "^11.0.0", - "@nestjs/schematics": "^11.0.0", - "@nestjs/testing": "^11.0.1", - "@types/express": "^5.0.0", - "@types/jest": "^29.5.14", - "@types/node": "^22.19.11", - "@types/nodemailer": "^7.0.11", - "@types/supertest": "^6.0.2", - "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.1", - "eslint-plugin-prettier": "^5.2.2", - "globals": "^16.0.0", - "jest": "^29.7.0", - "prettier": "^3.4.2", - "source-map-support": "^0.5.21", - "supertest": "^7.0.0", - "ts-jest": "^29.4.11", - "ts-loader": "^9.5.2", - "ts-node": "^10.9.2", - "tsconfig-paths": "^4.2.0", - "typescript": "^5.7.3", - "typescript-eslint": "^8.20.0" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": "src", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "collectCoverageFrom": [ - "**/*.(t|j)s" - ], - "coverageDirectory": "../coverage", - "testEnvironment": "node" - } -} diff --git a/apps/backend/pnpm-lock.yaml b/apps/backend/pnpm-lock.yaml deleted file mode 100644 index 8db5d74d..00000000 --- a/apps/backend/pnpm-lock.yaml +++ /dev/null @@ -1,8149 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@nestjs/common': - specifier: ^11.1.14 - version: 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/config': - specifier: ^4.0.3 - version: 4.0.3(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2) - '@nestjs/core': - specifier: ^11.0.1 - version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.17)(@nestjs/websockets@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/jwt': - specifier: ^11.0.2 - version: 11.0.2(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)) - '@nestjs/mapped-types': - specifier: ^2.1.0 - version: 2.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2) - '@nestjs/passport': - specifier: ^11.0.5 - version: 11.0.5(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0) - '@nestjs/platform-express': - specifier: ^11.0.1 - version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) - '@nestjs/platform-socket.io': - specifier: ^11.0.1 - version: 11.1.19(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.19)(rxjs@7.8.2) - '@nestjs/schedule': - specifier: ^6.1.1 - version: 6.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) - '@nestjs/swagger': - specifier: ^11.2.0 - version: 11.2.6(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2) - '@nestjs/throttler': - specifier: ^6.5.0 - version: 6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2) - '@nestjs/typeorm': - specifier: ^11.0.0 - version: 11.0.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.28(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3))) - '@nestjs/websockets': - specifier: ^11.0.1 - version: 11.1.19(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@stellar/stellar-sdk': - specifier: ^14.5.0 - version: 14.6.1 - '@types/bcrypt': - specifier: ^6.0.0 - version: 6.0.0 - '@types/passport-jwt': - specifier: ^4.0.1 - version: 4.0.1 - axios: - specifier: ^1.13.5 - version: 1.13.6 - bcrypt: - specifier: ^6.0.0 - version: 6.0.0 - class-transformer: - specifier: ^0.5.1 - version: 0.5.1 - class-validator: - specifier: ^0.14.3 - version: 0.14.4 - dotenv: - specifier: ^17.3.1 - version: 17.3.1 - nodemailer: - specifier: ^8.0.3 - version: 8.0.4 - passport: - specifier: ^0.7.0 - version: 0.7.0 - passport-jwt: - specifier: ^4.0.1 - version: 4.0.1 - reflect-metadata: - specifier: ^0.2.2 - version: 0.2.2 - rxjs: - specifier: ^7.8.1 - version: 7.8.2 - socket.io: - specifier: ^4.8.3 - version: 4.8.3 - sqlite3: - specifier: ^5.1.7 - version: 5.1.7 - stellar-sdk: - specifier: ^13.3.0 - version: 13.3.0 - swagger-ui-express: - specifier: ^5.0.1 - version: 5.0.1(express@5.2.1) - typeorm: - specifier: ^0.3.28 - version: 0.3.28(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)) - devDependencies: - '@eslint/eslintrc': - specifier: ^3.2.0 - version: 3.3.5 - '@eslint/js': - specifier: ^9.18.0 - version: 9.39.4 - '@nestjs/cli': - specifier: ^11.0.0 - version: 11.0.16(@types/node@22.19.15) - '@nestjs/schematics': - specifier: ^11.0.0 - version: 11.0.9(chokidar@4.0.3)(typescript@5.9.3) - '@nestjs/testing': - specifier: ^11.0.1 - version: 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-express@11.1.17) - '@types/express': - specifier: ^5.0.0 - version: 5.0.6 - '@types/jest': - specifier: ^30.0.0 - version: 30.0.0 - '@types/node': - specifier: ^22.19.11 - version: 22.19.15 - '@types/nodemailer': - specifier: ^7.0.11 - version: 7.0.11 - '@types/supertest': - specifier: ^6.0.2 - version: 6.0.3 - eslint: - specifier: ^9.18.0 - version: 9.39.4 - eslint-config-prettier: - specifier: ^10.0.1 - version: 10.1.8(eslint@9.39.4) - eslint-plugin-prettier: - specifier: ^5.2.2 - version: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.4))(eslint@9.39.4)(prettier@3.8.1) - globals: - specifier: ^16.0.0 - version: 16.5.0 - jest: - specifier: ^30.0.0 - version: 30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)) - prettier: - specifier: ^3.4.2 - version: 3.8.1 - source-map-support: - specifier: ^0.5.21 - version: 0.5.21 - supertest: - specifier: ^7.0.0 - version: 7.2.2 - ts-jest: - specifier: ^29.2.5 - version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)))(typescript@5.9.3) - ts-loader: - specifier: ^9.5.2 - version: 9.5.4(typescript@5.9.3)(webpack@5.104.1) - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@22.19.15)(typescript@5.9.3) - tsconfig-paths: - specifier: ^4.2.0 - version: 4.2.0 - typescript: - specifier: ^5.7.3 - version: 5.9.3 - typescript-eslint: - specifier: ^8.20.0 - version: 8.57.2(eslint@9.39.4)(typescript@5.9.3) - -packages: - - '@angular-devkit/core@19.2.17': - resolution: {integrity: sha512-Ah008x2RJkd0F+NLKqIpA34/vUGwjlprRCkvddjDopAWRzYn6xCkz1Tqwuhn0nR1Dy47wTLKYD999TYl5ONOAQ==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - peerDependencies: - chokidar: ^4.0.0 - peerDependenciesMeta: - chokidar: - optional: true - - '@angular-devkit/core@19.2.19': - resolution: {integrity: sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - peerDependencies: - chokidar: ^4.0.0 - peerDependenciesMeta: - chokidar: - optional: true - - '@angular-devkit/schematics-cli@19.2.19': - resolution: {integrity: sha512-7q9UY6HK6sccL9F3cqGRUwKhM7b/XfD2YcVaZ2WD7VMaRlRm85v6mRjSrfKIAwxcQU0UK27kMc79NIIqaHjzxA==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - hasBin: true - - '@angular-devkit/schematics@19.2.17': - resolution: {integrity: sha512-ADfbaBsrG8mBF6Mfs+crKA/2ykB8AJI50Cv9tKmZfwcUcyAdmTr+vVvhsBCfvUAEokigSsgqgpYxfkJVxhJYeg==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - - '@angular-devkit/schematics@19.2.19': - resolution: {integrity: sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.29.1': - resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.29.2': - resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.29.2': - resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/plugin-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.28.6': - resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.28.6': - resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.28.6': - resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} - engines: {node: '>=6.9.0'} - - '@bcoe/v8-coverage@0.2.3': - resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - - '@borewit/text-codec@0.2.2': - resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} - - '@colors/colors@1.5.0': - resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} - engines: {node: '>=0.1.90'} - - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - - '@emnapi/core@1.9.1': - resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} - - '@emnapi/runtime@1.9.1': - resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} - - '@emnapi/wasi-threads@1.2.0': - resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} - - '@eslint-community/eslint-utils@4.9.1': - resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.2': - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/config-array@0.21.2': - resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.4.2': - resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.17.0': - resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/eslintrc@3.3.5': - resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.39.4': - resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.7': - resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.4.1': - resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@gar/promisify@1.1.3': - resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} - engines: {node: '>=18.18.0'} - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/retry@0.4.3': - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} - engines: {node: '>=18.18'} - - '@inquirer/ansi@1.0.2': - resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} - engines: {node: '>=18'} - - '@inquirer/checkbox@4.3.2': - resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/confirm@5.1.21': - resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/core@10.3.2': - resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/editor@4.2.23': - resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/expand@4.0.23': - resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/figures@1.0.15': - resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} - engines: {node: '>=18'} - - '@inquirer/input@4.3.1': - resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/number@3.0.23': - resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/password@4.0.23': - resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/prompts@7.10.1': - resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/prompts@7.3.2': - resolution: {integrity: sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/rawlist@4.1.11': - resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/search@3.2.2': - resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/select@4.4.2': - resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/type@3.0.10': - resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - '@jest/console@30.3.0': - resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/core@30.3.0': - resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/diff-sequences@30.3.0': - resolution: {integrity: sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/environment@30.3.0': - resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/expect-utils@30.3.0': - resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/expect@30.3.0': - resolution: {integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/fake-timers@30.3.0': - resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/get-type@30.1.0': - resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/globals@30.3.0': - resolution: {integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/pattern@30.0.1': - resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/reporters@30.3.0': - resolution: {integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/schemas@30.0.5': - resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/snapshot-utils@30.3.0': - resolution: {integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/source-map@30.0.1': - resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/test-result@30.3.0': - resolution: {integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/test-sequencer@30.3.0': - resolution: {integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/transform@30.3.0': - resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/types@30.3.0': - resolution: {integrity: sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/remapping@2.3.5': - resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/source-map@0.3.11': - resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - - '@lukeed/csprng@1.1.0': - resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} - engines: {node: '>=8'} - - '@microsoft/tsdoc@0.16.0': - resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} - - '@napi-rs/wasm-runtime@0.2.12': - resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - - '@nestjs/cli@11.0.16': - resolution: {integrity: sha512-P0H+Vcjki6P5160E5QnMt3Q0X5FTg4PZkP99Ig4lm/4JWqfw32j3EXv3YBTJ2DmxLwOQ/IS9F7dzKpMAgzKTGg==} - engines: {node: '>= 20.11'} - hasBin: true - peerDependencies: - '@swc/cli': ^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 - '@swc/core': ^1.3.62 - peerDependenciesMeta: - '@swc/cli': - optional: true - '@swc/core': - optional: true - - '@nestjs/common@11.1.17': - resolution: {integrity: sha512-hLODw5Abp8OQgA+mUO4tHou4krKgDtUcM9j5Ihxncst9XeyxYBTt2bwZm4e4EQr5E352S4Fyy6V3iFx9ggxKAg==} - peerDependencies: - class-transformer: '>=0.4.1' - class-validator: '>=0.13.2' - reflect-metadata: ^0.1.12 || ^0.2.0 - rxjs: ^7.1.0 - peerDependenciesMeta: - class-transformer: - optional: true - class-validator: - optional: true - - '@nestjs/config@4.0.3': - resolution: {integrity: sha512-FQ3M3Ohqfl+nHAn5tp7++wUQw0f2nAk+SFKe8EpNRnIifPqvfJP6JQxPKtFLMOHbyer4X646prFG4zSRYEssQQ==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - rxjs: ^7.1.0 - - '@nestjs/core@11.1.17': - resolution: {integrity: sha512-lD5mAYekTTurF3vDaa8C2OKPnjiz4tsfxIc5XlcSUzOhkwWf6Ay3HKvt6FmvuWQam6uIIHX52Clg+e6tAvf/cg==} - engines: {node: '>= 20'} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/microservices': ^11.0.0 - '@nestjs/platform-express': ^11.0.0 - '@nestjs/websockets': ^11.0.0 - reflect-metadata: ^0.1.12 || ^0.2.0 - rxjs: ^7.1.0 - peerDependenciesMeta: - '@nestjs/microservices': - optional: true - '@nestjs/platform-express': - optional: true - '@nestjs/websockets': - optional: true - - '@nestjs/jwt@11.0.2': - resolution: {integrity: sha512-rK8aE/3/Ma45gAWfCksAXUNbOoSOUudU0Kn3rT39htPF7wsYXtKfjALKeKKJbFrIWbLjsbqfXX5bIJNvgBugGA==} - peerDependencies: - '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - - '@nestjs/mapped-types@2.1.0': - resolution: {integrity: sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - class-transformer: ^0.4.0 || ^0.5.0 - class-validator: ^0.13.0 || ^0.14.0 - reflect-metadata: ^0.1.12 || ^0.2.0 - peerDependenciesMeta: - class-transformer: - optional: true - class-validator: - optional: true - - '@nestjs/passport@11.0.5': - resolution: {integrity: sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - passport: ^0.5.0 || ^0.6.0 || ^0.7.0 - - '@nestjs/platform-express@11.1.17': - resolution: {integrity: sha512-mAf4eOsSBsTOn/VbrUO1gsjW6dVh91qqXPMXun4dN8SnNjf7PTQagM9o8d6ab8ZBpNe6UdZftdrZoDetU+n4Qg==} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/core': ^11.0.0 - - '@nestjs/platform-socket.io@11.1.19': - resolution: {integrity: sha512-gu1nPIEaP5Qjjg/Cl8wXyvwGpdZGzgbtK4KcH65YRAA+GTKUkIHb4BNpLJ27Ymq/wqLJKNEbCjajfzD0BEjMGA==} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/websockets': ^11.0.0 - rxjs: ^7.1.0 - - '@nestjs/schedule@6.1.1': - resolution: {integrity: sha512-kQl1RRgi02GJ0uaUGCrXHCcwISsCsJDciCKe38ykJZgnAeeoeVWs8luWtBo4AqAAXm4nS5K8RlV0smHUJ4+2FA==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - '@nestjs/core': ^10.0.0 || ^11.0.0 - - '@nestjs/schematics@11.0.9': - resolution: {integrity: sha512-0NfPbPlEaGwIT8/TCThxLzrlz3yzDNkfRNpbL7FiplKq3w4qXpJg0JYwqgMEJnLQZm3L/L/5XjoyfJHUO3qX9g==} - peerDependencies: - typescript: '>=4.8.2' - - '@nestjs/swagger@11.2.6': - resolution: {integrity: sha512-oiXOxMQqDFyv1AKAqFzSo6JPvMEs4uA36Eyz/s2aloZLxUjcLfUMELSLSNQunr61xCPTpwEOShfmO7NIufKXdA==} - peerDependencies: - '@fastify/static': ^8.0.0 || ^9.0.0 - '@nestjs/common': ^11.0.1 - '@nestjs/core': ^11.0.1 - class-transformer: '*' - class-validator: '*' - reflect-metadata: ^0.1.12 || ^0.2.0 - peerDependenciesMeta: - '@fastify/static': - optional: true - class-transformer: - optional: true - class-validator: - optional: true - - '@nestjs/testing@11.1.17': - resolution: {integrity: sha512-lNffw+z+2USewmw4W0tsK+Rq94A2N4PiHbcqoRUu5y8fnqxQeIWGHhjo5BFCqj7eivqJBhT7WdRydxVq4rAHzg==} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/core': ^11.0.0 - '@nestjs/microservices': ^11.0.0 - '@nestjs/platform-express': ^11.0.0 - peerDependenciesMeta: - '@nestjs/microservices': - optional: true - '@nestjs/platform-express': - optional: true - - '@nestjs/throttler@6.5.0': - resolution: {integrity: sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==} - peerDependencies: - '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - reflect-metadata: ^0.1.13 || ^0.2.0 - - '@nestjs/typeorm@11.0.0': - resolution: {integrity: sha512-SOeUQl70Lb2OfhGkvnh4KXWlsd+zA08RuuQgT7kKbzivngxzSo1Oc7Usu5VxCxACQC9wc2l9esOHILSJeK7rJA==} - peerDependencies: - '@nestjs/common': ^10.0.0 || ^11.0.0 - '@nestjs/core': ^10.0.0 || ^11.0.0 - reflect-metadata: ^0.1.13 || ^0.2.0 - rxjs: ^7.2.0 - typeorm: ^0.3.0 - - '@nestjs/websockets@11.1.19': - resolution: {integrity: sha512-2qo8jtIwwwgkqAI1BtnJ02EaFLrRkKA39eYXS8IhZCHilhBHCWdjnJ5cLcFq4oF+s+KZ7LcLGD/3stxJy8ijzg==} - peerDependencies: - '@nestjs/common': ^11.0.0 - '@nestjs/core': ^11.0.0 - '@nestjs/platform-socket.io': ^11.0.0 - reflect-metadata: ^0.1.12 || ^0.2.0 - rxjs: ^7.1.0 - peerDependenciesMeta: - '@nestjs/platform-socket.io': - optional: true - - '@noble/curves@1.9.7': - resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} - engines: {node: ^14.21.3 || >=16} - - '@noble/hashes@1.8.0': - resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} - engines: {node: ^14.21.3 || >=16} - - '@npmcli/fs@1.1.1': - resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} - - '@npmcli/move-file@1.1.2': - resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} - engines: {node: '>=10'} - deprecated: This functionality has been moved to @npmcli/fs - - '@nuxt/opencollective@0.4.1': - resolution: {integrity: sha512-GXD3wy50qYbxCJ652bDrDzgMr3NFEkIS374+IgFQKkCvk9yiYcLvX2XDYr7UyQxf4wK0e+yqDYRubZ0DtOxnmQ==} - engines: {node: ^14.18.0 || >=16.10.0, npm: '>=5.10.0'} - hasBin: true - - '@paralleldrive/cuid2@2.3.1': - resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@pkgr/core@0.2.9': - resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - - '@scarf/scarf@1.4.0': - resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} - - '@sinclair/typebox@0.34.48': - resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} - - '@sinonjs/commons@3.0.1': - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - - '@sinonjs/fake-timers@15.1.1': - resolution: {integrity: sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==} - - '@socket.io/component-emitter@3.1.2': - resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - - '@sqltools/formatter@1.2.5': - resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} - - '@stellar/js-xdr@3.1.2': - resolution: {integrity: sha512-VVolPL5goVEIsvuGqDc5uiKxV03lzfWdvYg1KikvwheDmTBO68CKDji3bAZ/kppZrx5iTA8z3Ld5yuytcvhvOQ==} - - '@stellar/stellar-base@13.1.0': - resolution: {integrity: sha512-90EArG+eCCEzDGj3OJNoCtwpWDwxjv+rs/RNPhvg4bulpjN/CSRj+Ys/SalRcfM4/WRC5/qAfjzmJBAuquWhkA==} - engines: {node: '>=18.0.0'} - - '@stellar/stellar-base@14.1.0': - resolution: {integrity: sha512-A8kFli6QGy22SRF45IjgPAJfUNGjnI+R7g4DF5NZYVsD1kGf7B4ITyc4OPclLV9tqNI4/lXxafGEw0JEUbHixw==} - engines: {node: '>=20.0.0'} - - '@stellar/stellar-sdk@14.6.1': - resolution: {integrity: sha512-A1rQWDLdUasXkMXnYSuhgep+3ZZzyuXJKdt5/KAIc0gkmSp906HTvUpbT4pu+bVr41tu0+J4Ugz9J4BQAGGytg==} - engines: {node: '>=20.0.0'} - hasBin: true - - '@tokenizer/inflate@0.4.1': - resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} - engines: {node: '>=18'} - - '@tokenizer/token@0.3.0': - resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - - '@tootallnate/once@1.1.2': - resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==} - engines: {node: '>= 6'} - - '@tsconfig/node10@1.0.12': - resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} - - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - - '@types/bcrypt@6.0.0': - resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==} - - '@types/body-parser@1.19.6': - resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} - - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - - '@types/cookiejar@2.1.5': - resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} - - '@types/cors@2.8.19': - resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} - - '@types/eslint-scope@3.7.7': - resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} - - '@types/eslint@9.6.1': - resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/express-serve-static-core@5.1.1': - resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} - - '@types/express@5.0.6': - resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} - - '@types/http-errors@2.0.5': - resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} - - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - - '@types/jest@30.0.0': - resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/jsonwebtoken@9.0.10': - resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} - - '@types/luxon@3.7.1': - resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==} - - '@types/methods@1.1.4': - resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} - - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - - '@types/node@22.19.15': - resolution: {integrity: sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==} - - '@types/nodemailer@7.0.11': - resolution: {integrity: sha512-E+U4RzR2dKrx+u3N4DlsmLaDC6mMZOM/TPROxA0UAPiTgI0y4CEFBmZE+coGWTjakDriRsXG368lNk1u9Q0a2g==} - - '@types/passport-jwt@4.0.1': - resolution: {integrity: sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==} - - '@types/passport-strategy@0.2.38': - resolution: {integrity: sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==} - - '@types/passport@1.0.17': - resolution: {integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==} - - '@types/qs@6.15.0': - resolution: {integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==} - - '@types/range-parser@1.2.7': - resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - - '@types/send@1.2.1': - resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} - - '@types/serve-static@2.2.0': - resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} - - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - - '@types/superagent@8.1.9': - resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} - - '@types/supertest@6.0.3': - resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} - - '@types/validator@13.15.10': - resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} - - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - - '@types/yargs@17.0.35': - resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - - '@typescript-eslint/eslint-plugin@8.57.2': - resolution: {integrity: sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.57.2 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/parser@8.57.2': - resolution: {integrity: sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/project-service@8.57.2': - resolution: {integrity: sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/scope-manager@8.57.2': - resolution: {integrity: sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/tsconfig-utils@8.57.2': - resolution: {integrity: sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/type-utils@8.57.2': - resolution: {integrity: sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/types@8.57.2': - resolution: {integrity: sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.57.2': - resolution: {integrity: sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/utils@8.57.2': - resolution: {integrity: sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/visitor-keys@8.57.2': - resolution: {integrity: sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - - '@unrs/resolver-binding-android-arm-eabi@1.11.1': - resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} - cpu: [arm] - os: [android] - - '@unrs/resolver-binding-android-arm64@1.11.1': - resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} - cpu: [arm64] - os: [android] - - '@unrs/resolver-binding-darwin-arm64@1.11.1': - resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} - cpu: [arm64] - os: [darwin] - - '@unrs/resolver-binding-darwin-x64@1.11.1': - resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} - cpu: [x64] - os: [darwin] - - '@unrs/resolver-binding-freebsd-x64@1.11.1': - resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} - cpu: [x64] - os: [freebsd] - - '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': - resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} - cpu: [arm] - os: [linux] - - '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': - resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} - cpu: [arm] - os: [linux] - - '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': - resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@unrs/resolver-binding-linux-arm64-musl@1.11.1': - resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': - resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': - resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': - resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': - resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@unrs/resolver-binding-linux-x64-gnu@1.11.1': - resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@unrs/resolver-binding-linux-x64-musl@1.11.1': - resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@unrs/resolver-binding-wasm32-wasi@1.11.1': - resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] - - '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': - resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} - cpu: [arm64] - os: [win32] - - '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': - resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} - cpu: [ia32] - os: [win32] - - '@unrs/resolver-binding-win32-x64-msvc@1.11.1': - resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} - cpu: [x64] - os: [win32] - - '@webassemblyjs/ast@1.14.1': - resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} - - '@webassemblyjs/floating-point-hex-parser@1.13.2': - resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} - - '@webassemblyjs/helper-api-error@1.13.2': - resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} - - '@webassemblyjs/helper-buffer@1.14.1': - resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} - - '@webassemblyjs/helper-numbers@1.13.2': - resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} - - '@webassemblyjs/helper-wasm-bytecode@1.13.2': - resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} - - '@webassemblyjs/helper-wasm-section@1.14.1': - resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} - - '@webassemblyjs/ieee754@1.13.2': - resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} - - '@webassemblyjs/leb128@1.13.2': - resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} - - '@webassemblyjs/utf8@1.13.2': - resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} - - '@webassemblyjs/wasm-edit@1.14.1': - resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} - - '@webassemblyjs/wasm-gen@1.14.1': - resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} - - '@webassemblyjs/wasm-opt@1.14.1': - resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} - - '@webassemblyjs/wasm-parser@1.14.1': - resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} - - '@webassemblyjs/wast-printer@1.14.1': - resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} - - '@xtuc/ieee754@1.2.0': - resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} - - '@xtuc/long@4.2.2': - resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} - - abbrev@1.1.1: - resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} - - accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} - - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - - acorn-import-phases@1.0.4: - resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} - engines: {node: '>=10.13.0'} - peerDependencies: - acorn: ^8.14.0 - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn-walk@8.3.5: - resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} - engines: {node: '>=0.4.0'} - - acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} - engines: {node: '>=0.4.0'} - hasBin: true - - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - - agentkeepalive@4.6.0: - resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} - engines: {node: '>= 8.0.0'} - - aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv-keywords@3.5.2: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} - peerDependencies: - ajv: ^6.9.1 - - ajv-keywords@5.1.0: - resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} - peerDependencies: - ajv: ^8.8.2 - - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} - - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - - ansis@4.2.0: - resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} - engines: {node: '>=14'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - app-root-path@3.1.0: - resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} - engines: {node: '>= 6.0.0'} - - append-field@1.0.0: - resolution: {integrity: sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==} - - aproba@2.1.0: - resolution: {integrity: sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==} - - are-we-there-yet@3.0.1: - resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. - - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - array-timsort@1.0.3: - resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} - - asap@2.0.6: - resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - axios@1.13.6: - resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} - - babel-jest@30.3.0: - resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@babel/core': ^7.11.0 || ^8.0.0-0 - - babel-plugin-istanbul@7.0.1: - resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} - engines: {node: '>=12'} - - babel-plugin-jest-hoist@30.3.0: - resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - babel-preset-current-node-syntax@1.2.0: - resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} - peerDependencies: - '@babel/core': ^7.0.0 || ^8.0.0-0 - - babel-preset-jest@30.3.0: - resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@babel/core': ^7.11.0 || ^8.0.0-beta.1 - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - balanced-match@4.0.4: - resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} - engines: {node: 18 || 20 || >=22} - - bare-addon-resolve@1.10.0: - resolution: {integrity: sha512-sSd0jieRJlDaODOzj0oe0RjFVC1QI0ZIjGIdPkbrTXsdVVtENg14c+lHHAhHwmWCZ2nQlMhy8jA3Y5LYPc/isA==} - peerDependencies: - bare-url: '*' - peerDependenciesMeta: - bare-url: - optional: true - - bare-module-resolve@1.12.1: - resolution: {integrity: sha512-hbmAPyFpEq8FoZMd5sFO3u6MC5feluWoGE8YKlA8fCrl6mNtx68Wjg4DTiDJcqRJaovTvOYKfYngoBUnbaT7eg==} - peerDependencies: - bare-url: '*' - peerDependenciesMeta: - bare-url: - optional: true - - bare-semver@1.0.2: - resolution: {integrity: sha512-ESVaN2nzWhcI5tf3Zzcq9aqCZ676VWzqw07eEZ0qxAcEOAFYBa0pWq8sK34OQeHLY3JsfKXZS9mDyzyxGjeLzA==} - - base32.js@0.1.0: - resolution: {integrity: sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==} - engines: {node: '>=0.12.0'} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - base64id@2.0.0: - resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} - engines: {node: ^4.5.0 || >= 5.9} - - baseline-browser-mapping@2.10.10: - resolution: {integrity: sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==} - engines: {node: '>=6.0.0'} - hasBin: true - - bcrypt@6.0.0: - resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==} - engines: {node: '>= 18'} - - bignumber.js@9.3.1: - resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} - - bindings@1.5.0: - resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - body-parser@2.2.2: - resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} - engines: {node: '>=18'} - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - - brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} - engines: {node: 18 || 20 || >=22} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - bs-logger@0.2.6: - resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} - engines: {node: '>= 6'} - - bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} - - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - - cacache@15.3.0: - resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} - engines: {node: '>= 10'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - caniuse-lite@1.0.30001781: - resolution: {integrity: sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - - chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - - chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - - chrome-trace-event@1.0.4: - resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} - engines: {node: '>=6.0'} - - ci-info@4.4.0: - resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} - engines: {node: '>=8'} - - cjs-module-lexer@2.2.0: - resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} - - class-transformer@0.5.1: - resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} - - class-validator@0.14.4: - resolution: {integrity: sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==} - - clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - - cli-table3@0.6.5: - resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} - engines: {node: 10.* || >= 12.*} - - cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - - co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - - collect-v8-coverage@1.0.3: - resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - color-support@1.1.3: - resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} - hasBin: true - - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - - commander@14.0.3: - resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} - engines: {node: '>=20'} - - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - - comment-json@4.4.1: - resolution: {integrity: sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==} - engines: {node: '>= 6'} - - component-emitter@1.3.1: - resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - concat-stream@2.0.0: - resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} - engines: {'0': node >= 6.0} - - consola@3.4.2: - resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} - engines: {node: ^14.18.0 || >=16.10.0} - - console-control-strings@1.1.0: - resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} - - content-disposition@1.0.1: - resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} - engines: {node: '>=18'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - - cookiejar@2.1.4: - resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - cors@2.8.6: - resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} - engines: {node: '>= 0.10'} - - cosmiconfig@8.3.6: - resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - - cron@4.4.0: - resolution: {integrity: sha512-fkdfq+b+AHI4cKdhZlppHveI/mgz2qpiYxcm+t5E5TsxX7QrLS1VE0+7GENEk9z0EeGPcpSciGv6ez24duWhwQ==} - engines: {node: '>=18.x'} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - dayjs@1.11.20: - resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - dedent@1.7.2: - resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - delegates@1.0.0: - resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} - - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - - dezalgo@1.0.4: - resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} - - diff@4.0.4: - resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} - engines: {node: '>=0.3.1'} - - dotenv-expand@12.0.3: - resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==} - engines: {node: '>=12'} - - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - - dotenv@17.2.3: - resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} - engines: {node: '>=12'} - - dotenv@17.3.1: - resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} - engines: {node: '>=12'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - - electron-to-chromium@1.5.325: - resolution: {integrity: sha512-PwfIw7WQSt3xX7yOf5OE/unLzsK9CaN2f/FvV3WjPR1Knoc1T9vePRVV4W1EM301JzzysK51K7FNKcusCr0zYA==} - - emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - - encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - engine.io-parser@5.2.3: - resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} - engines: {node: '>=10.0.0'} - - engine.io@6.6.7: - resolution: {integrity: sha512-DgOngfDKM2EviOH3Mr9m7ks1q8roetLy/IMmYthAYzbpInMbYc/GS+fWFA3rl1gvwKVsQrVV61fo5emD1y3OJQ==} - engines: {node: '>=10.2.0'} - - enhanced-resolve@5.20.1: - resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} - engines: {node: '>=10.13.0'} - - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - - err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-module-lexer@2.0.0: - resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - eslint-config-prettier@10.1.8: - resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - - eslint-plugin-prettier@5.5.5: - resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true - - eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - - eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@5.0.1: - resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - eslint@9.39.4: - resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - - espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - esquery@1.7.0: - resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - eventsource@2.0.2: - resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==} - engines: {node: '>=12.0.0'} - - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - exit-x@0.2.2: - resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} - engines: {node: '>= 0.8.0'} - - expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - - expect@30.3.0: - resolution: {integrity: sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - - fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - feaxios@0.0.23: - resolution: {integrity: sha512-eghR0A21fvbkcQBgZuMfQhrXxJzC0GNUGC9fXhBge33D+mFDTwl0aJ35zoQQn575BhyjQitRc5N4f+L4cP708g==} - - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - - file-type@21.3.2: - resolution: {integrity: sha512-DLkUvGwep3poOV2wpzbHCOnSKGk1LzyXTv+aHFgN2VFl96wnp8YA9YjO2qPzg5PuL8q/SW9Pdi6WTkYOIh995w==} - engines: {node: '>=20'} - - file-uri-to-path@1.0.0: - resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - - flatted@3.4.2: - resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} - - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - - fork-ts-checker-webpack-plugin@9.1.0: - resolution: {integrity: sha512-mpafl89VFPJmhnJ1ssH+8wmM2b50n+Rew5x42NeI2U78aRWgtkEtGmctp7iT16UjquJTjorEmIfESj3DxdW84Q==} - engines: {node: '>=14.21.3'} - peerDependencies: - typescript: '>3.6.0' - webpack: ^5.11.0 - - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - - formidable@3.5.4: - resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} - engines: {node: '>=14.0.0'} - - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - - fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - - fs-monkey@1.1.0: - resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - gauge@4.0.4: - resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - github-from-package@0.0.0: - resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob-to-regexp@0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - - glob@10.5.0: - resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - hasBin: true - - glob@13.0.0: - resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} - engines: {node: 20 || >=22} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - - globals@16.5.0: - resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} - engines: {node: '>=18'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} - hasBin: true - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - has-unicode@2.0.1: - resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - - http-cache-semantics@4.2.0: - resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - - http-errors@2.0.1: - resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} - engines: {node: '>= 0.8'} - - http-proxy-agent@4.0.1: - resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==} - engines: {node: '>= 6'} - - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - - humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} - engines: {node: '>=0.10.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} - engines: {node: '>= 4'} - - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - - import-local@3.2.0: - resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} - engines: {node: '>=8'} - hasBin: true - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} - - infer-owner@1.0.4: - resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} - engines: {node: '>= 12'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - - is-lambda@1.0.1: - resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - - is-retry-allowed@3.0.0: - resolution: {integrity: sha512-9xH0xvoggby+u0uGF7cZXdrutWiBiaFG8ZT4YFPXL8NzkyAwX3AKGLeFQLvzDpM430+nDFBZ1LHkie/8ocL06A==} - engines: {node: '>=12'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@6.0.3: - resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} - engines: {node: '>=10'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@5.0.6: - resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} - engines: {node: '>=10'} - - istanbul-reports@3.2.0: - resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} - engines: {node: '>=8'} - - iterare@1.2.1: - resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} - engines: {node: '>=6'} - - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jest-changed-files@30.3.0: - resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-circus@30.3.0: - resolution: {integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-cli@30.3.0: - resolution: {integrity: sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - jest-config@30.3.0: - resolution: {integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@types/node': '*' - esbuild-register: '>=3.4.0' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - esbuild-register: - optional: true - ts-node: - optional: true - - jest-diff@30.3.0: - resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-docblock@30.2.0: - resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-each@30.3.0: - resolution: {integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-environment-node@30.3.0: - resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-haste-map@30.3.0: - resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-leak-detector@30.3.0: - resolution: {integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-matcher-utils@30.3.0: - resolution: {integrity: sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-message-util@30.3.0: - resolution: {integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-mock@30.3.0: - resolution: {integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - - jest-regex-util@30.0.1: - resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-resolve-dependencies@30.3.0: - resolution: {integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-resolve@30.3.0: - resolution: {integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-runner@30.3.0: - resolution: {integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-runtime@30.3.0: - resolution: {integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-snapshot@30.3.0: - resolution: {integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-util@30.3.0: - resolution: {integrity: sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-validate@30.3.0: - resolution: {integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-watcher@30.3.0: - resolution: {integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest-worker@27.5.1: - resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} - engines: {node: '>= 10.13.0'} - - jest-worker@30.3.0: - resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - jest@30.3.0: - resolution: {integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@3.14.2: - resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} - hasBin: true - - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - - jsonwebtoken@9.0.3: - resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} - engines: {node: '>=12', npm: '>=6'} - - jwa@2.0.1: - resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - - jws@4.0.1: - resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - libphonenumber-js@1.12.40: - resolution: {integrity: sha512-HKGs7GowShNls3Zh+7DTr6wYpPk5jC78l508yQQY3e8ZgJChM3A9JZghmMJZuK+5bogSfuTafpjksGSR3aMIEg==} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - load-esm@1.0.3: - resolution: {integrity: sha512-v5xlu8eHD1+6r8EHTg6hfmO97LN8ugKtiXcy5e6oN72iD2r6u0RPfLl6fxM+7Wnh2ZRq15o0russMst44WauPA==} - engines: {node: '>=13.2.0'} - - loader-runner@4.3.1: - resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} - engines: {node: '>=6.11.5'} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - - lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} - - lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} - - lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - - lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - - lodash.memoize@4.1.2: - resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} - - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@11.2.7: - resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} - engines: {node: 20 || >=22} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - - luxon@3.7.2: - resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} - engines: {node: '>=12'} - - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - make-fetch-happen@9.1.0: - resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==} - engines: {node: '>= 10'} - - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - - memfs@3.5.3: - resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} - engines: {node: '>= 4.0.0'} - - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mime-types@3.0.2: - resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} - engines: {node: '>=18'} - - mime@2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - minimatch@10.2.4: - resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} - engines: {node: 18 || 20 || >=22} - - minimatch@3.1.5: - resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass-collect@1.0.2: - resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} - engines: {node: '>= 8'} - - minipass-fetch@1.4.1: - resolution: {integrity: sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==} - engines: {node: '>=8'} - - minipass-flush@1.0.6: - resolution: {integrity: sha512-7Uf5gMJZ2kTkFisE3toGxT991s+cg+vMh42nbZGM2bNxfYVpkpqRudf1QrcOy72a3PwcL4JYqL+4NY7t0Hdd0A==} - engines: {node: '>=16 || 14 >=14.17'} - - minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} - - minipass-sized@1.0.3: - resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} - engines: {node: '>=8'} - - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - - minipass@7.1.3: - resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} - engines: {node: '>=16 || 14 >=14.17'} - - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - - mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - multer@2.1.1: - resolution: {integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==} - engines: {node: '>= 10.16.0'} - - mute-stream@2.0.0: - resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} - engines: {node: ^18.17.0 || >=20.5.0} - - napi-build-utils@2.0.0: - resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - - napi-postinstall@0.3.4: - resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - hasBin: true - - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - - negotiator@0.6.4: - resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} - engines: {node: '>= 0.6'} - - negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} - - neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - - node-abi@3.89.0: - resolution: {integrity: sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==} - engines: {node: '>=10'} - - node-abort-controller@3.1.1: - resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} - - node-addon-api@7.1.1: - resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - - node-addon-api@8.6.0: - resolution: {integrity: sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==} - engines: {node: ^18 || ^20 || >= 21} - - node-emoji@1.11.0: - resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} - - node-gyp-build@4.8.4: - resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} - hasBin: true - - node-gyp@8.4.1: - resolution: {integrity: sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==} - engines: {node: '>= 10.12.0'} - hasBin: true - - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - - node-releases@2.0.36: - resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} - - nodemailer@8.0.4: - resolution: {integrity: sha512-k+jf6N8PfQJ0Fe8ZhJlgqU5qJU44Lpvp2yvidH3vp1lPnVQMgi4yEEMPXg5eJS1gFIJTVq1NHBk7Ia9ARdSBdQ==} - engines: {node: '>=6.0.0'} - - nopt@5.0.0: - resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} - engines: {node: '>=6'} - hasBin: true - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - - npmlog@6.0.2: - resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - deprecated: This package is no longer supported. - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - - passport-jwt@4.0.1: - resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==} - - passport-strategy@1.0.0: - resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==} - engines: {node: '>= 0.4.0'} - - passport@0.7.0: - resolution: {integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==} - engines: {node: '>= 0.4.0'} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-scurry@2.0.2: - resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} - engines: {node: 18 || 20 || >=22} - - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pause@0.0.1: - resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} - engines: {node: '>=12'} - - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} - engines: {node: '>=12'} - - pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} - engines: {node: '>= 6'} - - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - - pluralize@8.0.0: - resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} - engines: {node: '>=4'} - - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - - prebuild-install@7.1.3: - resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} - engines: {node: '>=10'} - deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. - hasBin: true - - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier-linter-helpers@1.0.1: - resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} - engines: {node: '>=6.0.0'} - - prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} - engines: {node: '>=14'} - hasBin: true - - pretty-format@30.3.0: - resolution: {integrity: sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - promise-inflight@1.0.1: - resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true - - promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - pump@3.0.4: - resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - pure-rand@7.0.1: - resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} - - qs@6.15.0: - resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} - engines: {node: '>=0.6'} - - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - - range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - - raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} - - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - - reflect-metadata@0.2.2: - resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} - - require-addon@1.2.0: - resolution: {integrity: sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==} - engines: {bare: '>=1.10.0'} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - - retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - - rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - schema-utils@3.3.0: - resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} - engines: {node: '>= 10.13.0'} - - schema-utils@4.3.3: - resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} - engines: {node: '>= 10.13.0'} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - - send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} - engines: {node: '>= 18'} - - serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} - engines: {node: '>= 18'} - - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - sha.js@2.4.12: - resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} - engines: {node: '>= 0.10'} - hasBin: true - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - simple-get@4.0.1: - resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - socket.io-adapter@2.5.6: - resolution: {integrity: sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==} - - socket.io-parser@4.2.6: - resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} - engines: {node: '>=10.0.0'} - - socket.io@4.8.3: - resolution: {integrity: sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==} - engines: {node: '>=10.2.0'} - - socks-proxy-agent@6.2.1: - resolution: {integrity: sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==} - engines: {node: '>= 10'} - - socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - - sodium-native@4.3.3: - resolution: {integrity: sha512-OnxSlN3uyY8D0EsLHpmm2HOFmKddQVvEMmsakCrXUzSd8kjjbzL413t4ZNF3n0UxSwNgwTyUvkmZHTfuCeiYSw==} - - source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - source-map@0.7.4: - resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} - engines: {node: '>= 8'} - - source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - sql-highlight@6.1.0: - resolution: {integrity: sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==} - engines: {node: '>=14'} - - sqlite3@5.1.7: - resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} - - ssri@8.0.1: - resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} - engines: {node: '>= 8'} - - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - - statuses@2.0.2: - resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} - engines: {node: '>= 0.8'} - - stellar-sdk@13.3.0: - resolution: {integrity: sha512-jAA3+U7oAUueldoS4kuEhcym+DigElWq9isPxt7tjMrE7kTJ2vvY29waavUb2FSfQIWwGbuwAJTYddy2BeyJsw==} - engines: {node: '>=18.0.0'} - deprecated: ⚠️ This package has moved to @stellar/stellar-sdk! 🚚 - - streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - - string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.2.0: - resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} - engines: {node: '>=12'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - strtok3@10.3.5: - resolution: {integrity: sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==} - engines: {node: '>=18'} - - superagent@10.3.0: - resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==} - engines: {node: '>=14.18.0'} - - supertest@7.2.2: - resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==} - engines: {node: '>=14.18.0'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - swagger-ui-dist@5.31.0: - resolution: {integrity: sha512-zSUTIck02fSga6rc0RZP3b7J7wgHXwLea8ZjgLA3Vgnb8QeOl3Wou2/j5QkzSGeoz6HusP/coYuJl33aQxQZpg==} - - swagger-ui-dist@5.32.1: - resolution: {integrity: sha512-6HQoo7+j8PA2QqP5kgAb9dl1uxUjvR0SAoL/WUp1sTEvm0F6D5npgU2OGCLwl++bIInqGlEUQ2mpuZRZYtyCzQ==} - - swagger-ui-express@5.0.1: - resolution: {integrity: sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==} - engines: {node: '>= v0.10.32'} - peerDependencies: - express: '>=4.0.0 || >=5.0.0-beta' - - symbol-observable@4.0.0: - resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} - engines: {node: '>=0.10'} - - synckit@0.11.12: - resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} - engines: {node: ^14.18.0 || >=16.0.0} - - tapable@2.3.2: - resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==} - engines: {node: '>=6'} - - tar-fs@2.1.4: - resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - - terser-webpack-plugin@5.4.0: - resolution: {integrity: sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true - - terser@5.46.1: - resolution: {integrity: sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==} - engines: {node: '>=10'} - hasBin: true - - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} - - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - - to-buffer@1.2.2: - resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==} - engines: {node: '>= 0.4'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - token-types@6.1.2: - resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} - engines: {node: '>=14.16'} - - toml@3.0.0: - resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} - - ts-api-utils@2.5.0: - resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - - ts-jest@29.4.6: - resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} - engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/transform': ^29.0.0 || ^30.0.0 - '@jest/types': ^29.0.0 || ^30.0.0 - babel-jest: ^29.0.0 || ^30.0.0 - esbuild: '*' - jest: ^29.0.0 || ^30.0.0 - jest-util: ^29.0.0 || ^30.0.0 - typescript: '>=4.3 <6' - peerDependenciesMeta: - '@babel/core': - optional: true - '@jest/transform': - optional: true - '@jest/types': - optional: true - babel-jest: - optional: true - esbuild: - optional: true - jest-util: - optional: true - - ts-loader@9.5.4: - resolution: {integrity: sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - typescript: '*' - webpack: ^5.0.0 - - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - - tsconfig-paths-webpack-plugin@4.2.0: - resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} - engines: {node: '>=10.13.0'} - - tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} - engines: {node: '>=6'} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - - tweetnacl@1.0.3: - resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} - - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - - type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} - - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - - typeorm@0.3.28: - resolution: {integrity: sha512-6GH7wXhtfq2D33ZuRXYwIsl/qM5685WZcODZb7noOOcRMteM9KF2x2ap3H0EBjnSV0VO4gNAfJT5Ukp0PkOlvg==} - engines: {node: '>=16.13.0'} - hasBin: true - peerDependencies: - '@google-cloud/spanner': ^5.18.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - '@sap/hana-client': ^2.14.22 - better-sqlite3: ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0 - ioredis: ^5.0.4 - mongodb: ^5.8.0 || ^6.0.0 - mssql: ^9.1.1 || ^10.0.0 || ^11.0.0 || ^12.0.0 - mysql2: ^2.2.5 || ^3.0.1 - oracledb: ^6.3.0 - pg: ^8.5.1 - pg-native: ^3.0.0 - pg-query-stream: ^4.0.0 - redis: ^3.1.1 || ^4.0.0 || ^5.0.14 - sql.js: ^1.4.0 - sqlite3: ^5.0.3 - ts-node: ^10.7.0 - typeorm-aurora-data-api-driver: ^2.0.0 || ^3.0.0 - peerDependenciesMeta: - '@google-cloud/spanner': - optional: true - '@sap/hana-client': - optional: true - better-sqlite3: - optional: true - ioredis: - optional: true - mongodb: - optional: true - mssql: - optional: true - mysql2: - optional: true - oracledb: - optional: true - pg: - optional: true - pg-native: - optional: true - pg-query-stream: - optional: true - redis: - optional: true - sql.js: - optional: true - sqlite3: - optional: true - ts-node: - optional: true - typeorm-aurora-data-api-driver: - optional: true - - typescript-eslint@8.57.2: - resolution: {integrity: sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - uglify-js@3.19.3: - resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} - engines: {node: '>=0.8.0'} - hasBin: true - - uid@2.0.2: - resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} - engines: {node: '>=8'} - - uint8array-extras@1.5.0: - resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} - engines: {node: '>=18'} - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - unique-filename@1.1.1: - resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} - - unique-slug@2.0.2: - resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - - unrs-resolver@1.11.1: - resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - urijs@1.19.11: - resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} - hasBin: true - - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - - v8-to-istanbul@9.3.0: - resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} - engines: {node: '>=10.12.0'} - - validator@13.15.26: - resolution: {integrity: sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==} - engines: {node: '>= 0.10'} - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - - watchpack@2.5.1: - resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} - engines: {node: '>=10.13.0'} - - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - - webpack-node-externals@3.0.0: - resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} - engines: {node: '>=6'} - - webpack-sources@3.3.4: - resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} - engines: {node: '>=10.13.0'} - - webpack@5.104.1: - resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} - engines: {node: '>= 0.4'} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - wide-align@1.1.5: - resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - write-file-atomic@5.0.1: - resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - yoctocolors-cjs@2.1.3: - resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} - engines: {node: '>=18'} - -snapshots: - - '@angular-devkit/core@19.2.17(chokidar@4.0.3)': - dependencies: - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) - jsonc-parser: 3.3.1 - picomatch: 4.0.2 - rxjs: 7.8.1 - source-map: 0.7.4 - optionalDependencies: - chokidar: 4.0.3 - - '@angular-devkit/core@19.2.19(chokidar@4.0.3)': - dependencies: - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) - jsonc-parser: 3.3.1 - picomatch: 4.0.2 - rxjs: 7.8.1 - source-map: 0.7.4 - optionalDependencies: - chokidar: 4.0.3 - - '@angular-devkit/schematics-cli@19.2.19(@types/node@22.19.15)(chokidar@4.0.3)': - dependencies: - '@angular-devkit/core': 19.2.19(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) - '@inquirer/prompts': 7.3.2(@types/node@22.19.15) - ansi-colors: 4.1.3 - symbol-observable: 4.0.0 - yargs-parser: 21.1.1 - transitivePeerDependencies: - - '@types/node' - - chokidar - - '@angular-devkit/schematics@19.2.17(chokidar@4.0.3)': - dependencies: - '@angular-devkit/core': 19.2.17(chokidar@4.0.3) - jsonc-parser: 3.3.1 - magic-string: 0.30.17 - ora: 5.4.1 - rxjs: 7.8.1 - transitivePeerDependencies: - - chokidar - - '@angular-devkit/schematics@19.2.19(chokidar@4.0.3)': - dependencies: - '@angular-devkit/core': 19.2.19(chokidar@4.0.3) - jsonc-parser: 3.3.1 - magic-string: 0.30.17 - ora: 5.4.1 - rxjs: 7.8.1 - transitivePeerDependencies: - - chokidar - - '@babel/code-frame@7.29.0': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/compat-data@7.29.0': {} - - '@babel/core@7.29.0': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.29.2 - '@babel/parser': 7.29.2 - '@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.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.29.1': - dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - - '@babel/helper-compilation-targets@7.28.6': - dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-globals@7.28.0': {} - - '@babel/helper-module-imports@7.28.6': - dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 - transitivePeerDependencies: - - supports-color - - '@babel/helper-plugin-utils@7.28.6': {} - - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.28.5': {} - - '@babel/helper-validator-option@7.27.1': {} - - '@babel/helpers@7.29.2': - dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - - '@babel/parser@7.29.2': - dependencies: - '@babel/types': 7.29.0 - - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.28.6 - - '@babel/template@7.28.6': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 - - '@babel/traverse@7.29.0': - dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.29.0': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - - '@bcoe/v8-coverage@0.2.3': {} - - '@borewit/text-codec@0.2.2': {} - - '@colors/colors@1.5.0': - optional: true - - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@emnapi/core@1.9.1': - dependencies: - '@emnapi/wasi-threads': 1.2.0 - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.9.1': - dependencies: - tslib: 2.8.1 - optional: true - - '@emnapi/wasi-threads@1.2.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)': - dependencies: - eslint: 9.39.4 - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.12.2': {} - - '@eslint/config-array@0.21.2': - dependencies: - '@eslint/object-schema': 2.1.7 - debug: 4.4.3 - minimatch: 3.1.5 - transitivePeerDependencies: - - supports-color - - '@eslint/config-helpers@0.4.2': - dependencies: - '@eslint/core': 0.17.0 - - '@eslint/core@0.17.0': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/eslintrc@3.3.5': - dependencies: - ajv: 6.14.0 - debug: 4.4.3 - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.5 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@9.39.4': {} - - '@eslint/object-schema@2.1.7': {} - - '@eslint/plugin-kit@0.4.1': - dependencies: - '@eslint/core': 0.17.0 - levn: 0.4.1 - - '@gar/promisify@1.1.3': - optional: true - - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.7': - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.4.3 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.4.3': {} - - '@inquirer/ansi@1.0.2': {} - - '@inquirer/checkbox@4.3.2(@types/node@22.19.15)': - dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.15) - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/confirm@5.1.21(@types/node@22.19.15)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/core@10.3.2(@types/node@22.19.15)': - dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.15) - cli-width: 4.1.0 - mute-stream: 2.0.0 - signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/editor@4.2.23(@types/node@22.19.15)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/external-editor': 1.0.3(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/expand@4.0.23(@types/node@22.19.15)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/external-editor@1.0.3(@types/node@22.19.15)': - dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.2 - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/figures@1.0.15': {} - - '@inquirer/input@4.3.1(@types/node@22.19.15)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/number@3.0.23(@types/node@22.19.15)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/password@4.0.23(@types/node@22.19.15)': - dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/prompts@7.10.1(@types/node@22.19.15)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@22.19.15) - '@inquirer/confirm': 5.1.21(@types/node@22.19.15) - '@inquirer/editor': 4.2.23(@types/node@22.19.15) - '@inquirer/expand': 4.0.23(@types/node@22.19.15) - '@inquirer/input': 4.3.1(@types/node@22.19.15) - '@inquirer/number': 3.0.23(@types/node@22.19.15) - '@inquirer/password': 4.0.23(@types/node@22.19.15) - '@inquirer/rawlist': 4.1.11(@types/node@22.19.15) - '@inquirer/search': 3.2.2(@types/node@22.19.15) - '@inquirer/select': 4.4.2(@types/node@22.19.15) - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/prompts@7.3.2(@types/node@22.19.15)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@22.19.15) - '@inquirer/confirm': 5.1.21(@types/node@22.19.15) - '@inquirer/editor': 4.2.23(@types/node@22.19.15) - '@inquirer/expand': 4.0.23(@types/node@22.19.15) - '@inquirer/input': 4.3.1(@types/node@22.19.15) - '@inquirer/number': 3.0.23(@types/node@22.19.15) - '@inquirer/password': 4.0.23(@types/node@22.19.15) - '@inquirer/rawlist': 4.1.11(@types/node@22.19.15) - '@inquirer/search': 3.2.2(@types/node@22.19.15) - '@inquirer/select': 4.4.2(@types/node@22.19.15) - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/rawlist@4.1.11(@types/node@22.19.15)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/type': 3.0.10(@types/node@22.19.15) - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/search@3.2.2(@types/node@22.19.15)': - dependencies: - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.15) - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/select@4.4.2(@types/node@22.19.15)': - dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.19.15) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.19.15) - yoctocolors-cjs: 2.1.3 - optionalDependencies: - '@types/node': 22.19.15 - - '@inquirer/type@3.0.10(@types/node@22.19.15)': - optionalDependencies: - '@types/node': 22.19.15 - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.2.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@istanbuljs/load-nyc-config@1.1.0': - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.2 - resolve-from: 5.0.0 - - '@istanbuljs/schema@0.1.3': {} - - '@jest/console@30.3.0': - dependencies: - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - chalk: 4.1.2 - jest-message-util: 30.3.0 - jest-util: 30.3.0 - slash: 3.0.0 - - '@jest/core@30.3.0(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3))': - dependencies: - '@jest/console': 30.3.0 - '@jest/pattern': 30.0.1 - '@jest/reporters': 30.3.0 - '@jest/test-result': 30.3.0 - '@jest/transform': 30.3.0 - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 4.4.0 - exit-x: 0.2.2 - graceful-fs: 4.2.11 - jest-changed-files: 30.3.0 - jest-config: 30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)) - jest-haste-map: 30.3.0 - jest-message-util: 30.3.0 - jest-regex-util: 30.0.1 - jest-resolve: 30.3.0 - jest-resolve-dependencies: 30.3.0 - jest-runner: 30.3.0 - jest-runtime: 30.3.0 - jest-snapshot: 30.3.0 - jest-util: 30.3.0 - jest-validate: 30.3.0 - jest-watcher: 30.3.0 - pretty-format: 30.3.0 - slash: 3.0.0 - transitivePeerDependencies: - - babel-plugin-macros - - esbuild-register - - supports-color - - ts-node - - '@jest/diff-sequences@30.3.0': {} - - '@jest/environment@30.3.0': - dependencies: - '@jest/fake-timers': 30.3.0 - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - jest-mock: 30.3.0 - - '@jest/expect-utils@30.3.0': - dependencies: - '@jest/get-type': 30.1.0 - - '@jest/expect@30.3.0': - dependencies: - expect: 30.3.0 - jest-snapshot: 30.3.0 - transitivePeerDependencies: - - supports-color - - '@jest/fake-timers@30.3.0': - dependencies: - '@jest/types': 30.3.0 - '@sinonjs/fake-timers': 15.1.1 - '@types/node': 22.19.15 - jest-message-util: 30.3.0 - jest-mock: 30.3.0 - jest-util: 30.3.0 - - '@jest/get-type@30.1.0': {} - - '@jest/globals@30.3.0': - dependencies: - '@jest/environment': 30.3.0 - '@jest/expect': 30.3.0 - '@jest/types': 30.3.0 - jest-mock: 30.3.0 - transitivePeerDependencies: - - supports-color - - '@jest/pattern@30.0.1': - dependencies: - '@types/node': 22.19.15 - jest-regex-util: 30.0.1 - - '@jest/reporters@30.3.0': - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 30.3.0 - '@jest/test-result': 30.3.0 - '@jest/transform': 30.3.0 - '@jest/types': 30.3.0 - '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 22.19.15 - chalk: 4.1.2 - collect-v8-coverage: 1.0.3 - exit-x: 0.2.2 - glob: 10.5.0 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.2.0 - jest-message-util: 30.3.0 - jest-util: 30.3.0 - jest-worker: 30.3.0 - slash: 3.0.0 - string-length: 4.0.2 - v8-to-istanbul: 9.3.0 - transitivePeerDependencies: - - supports-color - - '@jest/schemas@30.0.5': - dependencies: - '@sinclair/typebox': 0.34.48 - - '@jest/snapshot-utils@30.3.0': - dependencies: - '@jest/types': 30.3.0 - chalk: 4.1.2 - graceful-fs: 4.2.11 - natural-compare: 1.4.0 - - '@jest/source-map@30.0.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - '@jest/test-result@30.3.0': - dependencies: - '@jest/console': 30.3.0 - '@jest/types': 30.3.0 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.3 - - '@jest/test-sequencer@30.3.0': - dependencies: - '@jest/test-result': 30.3.0 - graceful-fs: 4.2.11 - jest-haste-map: 30.3.0 - slash: 3.0.0 - - '@jest/transform@30.3.0': - dependencies: - '@babel/core': 7.29.0 - '@jest/types': 30.3.0 - '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 7.0.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 30.3.0 - jest-regex-util: 30.0.1 - jest-util: 30.3.0 - pirates: 4.0.7 - slash: 3.0.0 - write-file-atomic: 5.0.1 - transitivePeerDependencies: - - supports-color - - '@jest/types@30.3.0': - dependencies: - '@jest/pattern': 30.0.1 - '@jest/schemas': 30.0.5 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 22.19.15 - '@types/yargs': 17.0.35 - chalk: 4.1.2 - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/remapping@2.3.5': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/source-map@0.3.11': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@lukeed/csprng@1.1.0': {} - - '@microsoft/tsdoc@0.16.0': {} - - '@napi-rs/wasm-runtime@0.2.12': - dependencies: - '@emnapi/core': 1.9.1 - '@emnapi/runtime': 1.9.1 - '@tybys/wasm-util': 0.10.1 - optional: true - - '@nestjs/cli@11.0.16(@types/node@22.19.15)': - dependencies: - '@angular-devkit/core': 19.2.19(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) - '@angular-devkit/schematics-cli': 19.2.19(@types/node@22.19.15)(chokidar@4.0.3) - '@inquirer/prompts': 7.10.1(@types/node@22.19.15) - '@nestjs/schematics': 11.0.9(chokidar@4.0.3)(typescript@5.9.3) - ansis: 4.2.0 - chokidar: 4.0.3 - cli-table3: 0.6.5 - commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.104.1) - glob: 13.0.0 - node-emoji: 1.11.0 - ora: 5.4.1 - tsconfig-paths: 4.2.0 - tsconfig-paths-webpack-plugin: 4.2.0 - typescript: 5.9.3 - webpack: 5.104.1 - webpack-node-externals: 3.0.0 - transitivePeerDependencies: - - '@types/node' - - esbuild - - uglify-js - - webpack-cli - - '@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)': - dependencies: - file-type: 21.3.2 - iterare: 1.2.1 - load-esm: 1.0.3 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - tslib: 2.8.1 - uid: 2.0.2 - optionalDependencies: - class-transformer: 0.5.1 - class-validator: 0.14.4 - transitivePeerDependencies: - - supports-color - - '@nestjs/config@4.0.3(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - dotenv: 17.2.3 - dotenv-expand: 12.0.3 - lodash: 4.17.23 - rxjs: 7.8.2 - - '@nestjs/core@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.17)(@nestjs/websockets@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2)': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nuxt/opencollective': 0.4.1 - fast-safe-stringify: 2.1.1 - iterare: 1.2.1 - path-to-regexp: 8.3.0 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - tslib: 2.8.1 - uid: 2.0.2 - optionalDependencies: - '@nestjs/platform-express': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) - '@nestjs/websockets': 11.1.19(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - - '@nestjs/jwt@11.0.2(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@types/jsonwebtoken': 9.0.10 - jsonwebtoken: 9.0.3 - - '@nestjs/mapped-types@2.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - reflect-metadata: 0.2.2 - optionalDependencies: - class-transformer: 0.5.1 - class-validator: 0.14.4 - - '@nestjs/passport@11.0.5(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - passport: 0.7.0 - - '@nestjs/platform-express@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.17)(@nestjs/websockets@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - cors: 2.8.6 - express: 5.2.1 - multer: 2.1.1 - path-to-regexp: 8.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@nestjs/platform-socket.io@11.1.19(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.19)(rxjs@7.8.2)': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/websockets': 11.1.19(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - rxjs: 7.8.2 - socket.io: 4.8.3 - tslib: 2.8.1 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - '@nestjs/schedule@6.1.1(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.17)(@nestjs/websockets@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - cron: 4.4.0 - - '@nestjs/schematics@11.0.9(chokidar@4.0.3)(typescript@5.9.3)': - dependencies: - '@angular-devkit/core': 19.2.17(chokidar@4.0.3) - '@angular-devkit/schematics': 19.2.17(chokidar@4.0.3) - comment-json: 4.4.1 - jsonc-parser: 3.3.1 - pluralize: 8.0.0 - typescript: 5.9.3 - transitivePeerDependencies: - - chokidar - - '@nestjs/swagger@11.2.6(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)': - dependencies: - '@microsoft/tsdoc': 0.16.0 - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.17)(@nestjs/websockets@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/mapped-types': 2.1.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2) - js-yaml: 4.1.1 - lodash: 4.17.23 - path-to-regexp: 8.3.0 - reflect-metadata: 0.2.2 - swagger-ui-dist: 5.31.0 - optionalDependencies: - class-transformer: 0.5.1 - class-validator: 0.14.4 - - '@nestjs/testing@11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-express@11.1.17)': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.17)(@nestjs/websockets@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - tslib: 2.8.1 - optionalDependencies: - '@nestjs/platform-express': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17) - - '@nestjs/throttler@6.5.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.17)(@nestjs/websockets@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - reflect-metadata: 0.2.2 - - '@nestjs/typeorm@11.0.0(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(reflect-metadata@0.2.2)(rxjs@7.8.2)(typeorm@0.3.28(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)))': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.17)(@nestjs/websockets@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - typeorm: 0.3.28(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)) - - '@nestjs/websockets@11.1.19(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.17)(@nestjs/platform-socket.io@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2)': - dependencies: - '@nestjs/common': 11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.17(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.17)(@nestjs/websockets@11.1.19)(reflect-metadata@0.2.2)(rxjs@7.8.2) - iterare: 1.2.1 - object-hash: 3.0.0 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - tslib: 2.8.1 - optionalDependencies: - '@nestjs/platform-socket.io': 11.1.19(@nestjs/common@11.1.17(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.19)(rxjs@7.8.2) - - '@noble/curves@1.9.7': - dependencies: - '@noble/hashes': 1.8.0 - - '@noble/hashes@1.8.0': {} - - '@npmcli/fs@1.1.1': - dependencies: - '@gar/promisify': 1.1.3 - semver: 7.7.4 - optional: true - - '@npmcli/move-file@1.1.2': - dependencies: - mkdirp: 1.0.4 - rimraf: 3.0.2 - optional: true - - '@nuxt/opencollective@0.4.1': - dependencies: - consola: 3.4.2 - - '@paralleldrive/cuid2@2.3.1': - dependencies: - '@noble/hashes': 1.8.0 - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@pkgr/core@0.2.9': {} - - '@scarf/scarf@1.4.0': {} - - '@sinclair/typebox@0.34.48': {} - - '@sinonjs/commons@3.0.1': - dependencies: - type-detect: 4.0.8 - - '@sinonjs/fake-timers@15.1.1': - dependencies: - '@sinonjs/commons': 3.0.1 - - '@socket.io/component-emitter@3.1.2': {} - - '@sqltools/formatter@1.2.5': {} - - '@stellar/js-xdr@3.1.2': {} - - '@stellar/stellar-base@13.1.0': - dependencies: - '@stellar/js-xdr': 3.1.2 - base32.js: 0.1.0 - bignumber.js: 9.3.1 - buffer: 6.0.3 - sha.js: 2.4.12 - tweetnacl: 1.0.3 - optionalDependencies: - sodium-native: 4.3.3 - transitivePeerDependencies: - - bare-url - - '@stellar/stellar-base@14.1.0': - dependencies: - '@noble/curves': 1.9.7 - '@stellar/js-xdr': 3.1.2 - base32.js: 0.1.0 - bignumber.js: 9.3.1 - buffer: 6.0.3 - sha.js: 2.4.12 - - '@stellar/stellar-sdk@14.6.1': - dependencies: - '@stellar/stellar-base': 14.1.0 - axios: 1.13.6 - bignumber.js: 9.3.1 - commander: 14.0.3 - eventsource: 2.0.2 - feaxios: 0.0.23 - randombytes: 2.1.0 - toml: 3.0.0 - urijs: 1.19.11 - transitivePeerDependencies: - - debug - - '@tokenizer/inflate@0.4.1': - dependencies: - debug: 4.4.3 - token-types: 6.1.2 - transitivePeerDependencies: - - supports-color - - '@tokenizer/token@0.3.0': {} - - '@tootallnate/once@1.1.2': - optional: true - - '@tsconfig/node10@1.0.12': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - - '@tybys/wasm-util@0.10.1': - dependencies: - tslib: 2.8.1 - optional: true - - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 - - '@types/babel__generator@7.27.0': - dependencies: - '@babel/types': 7.29.0 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 - - '@types/babel__traverse@7.28.0': - dependencies: - '@babel/types': 7.29.0 - - '@types/bcrypt@6.0.0': - dependencies: - '@types/node': 22.19.15 - - '@types/body-parser@1.19.6': - dependencies: - '@types/connect': 3.4.38 - '@types/node': 22.19.15 - - '@types/connect@3.4.38': - dependencies: - '@types/node': 22.19.15 - - '@types/cookiejar@2.1.5': {} - - '@types/cors@2.8.19': - dependencies: - '@types/node': 22.19.15 - - '@types/eslint-scope@3.7.7': - dependencies: - '@types/eslint': 9.6.1 - '@types/estree': 1.0.8 - - '@types/eslint@9.6.1': - dependencies: - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - - '@types/estree@1.0.8': {} - - '@types/express-serve-static-core@5.1.1': - dependencies: - '@types/node': 22.19.15 - '@types/qs': 6.15.0 - '@types/range-parser': 1.2.7 - '@types/send': 1.2.1 - - '@types/express@5.0.6': - dependencies: - '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 5.1.1 - '@types/serve-static': 2.2.0 - - '@types/http-errors@2.0.5': {} - - '@types/istanbul-lib-coverage@2.0.6': {} - - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - - '@types/jest@30.0.0': - dependencies: - expect: 30.3.0 - pretty-format: 30.3.0 - - '@types/json-schema@7.0.15': {} - - '@types/jsonwebtoken@9.0.10': - dependencies: - '@types/ms': 2.1.0 - '@types/node': 22.19.15 - - '@types/luxon@3.7.1': {} - - '@types/methods@1.1.4': {} - - '@types/ms@2.1.0': {} - - '@types/node@22.19.15': - dependencies: - undici-types: 6.21.0 - - '@types/nodemailer@7.0.11': - dependencies: - '@types/node': 22.19.15 - - '@types/passport-jwt@4.0.1': - dependencies: - '@types/jsonwebtoken': 9.0.10 - '@types/passport-strategy': 0.2.38 - - '@types/passport-strategy@0.2.38': - dependencies: - '@types/express': 5.0.6 - '@types/passport': 1.0.17 - - '@types/passport@1.0.17': - dependencies: - '@types/express': 5.0.6 - - '@types/qs@6.15.0': {} - - '@types/range-parser@1.2.7': {} - - '@types/send@1.2.1': - dependencies: - '@types/node': 22.19.15 - - '@types/serve-static@2.2.0': - dependencies: - '@types/http-errors': 2.0.5 - '@types/node': 22.19.15 - - '@types/stack-utils@2.0.3': {} - - '@types/superagent@8.1.9': - dependencies: - '@types/cookiejar': 2.1.5 - '@types/methods': 1.1.4 - '@types/node': 22.19.15 - form-data: 4.0.5 - - '@types/supertest@6.0.3': - dependencies: - '@types/methods': 1.1.4 - '@types/superagent': 8.1.9 - - '@types/validator@13.15.10': {} - - '@types/ws@8.18.1': - dependencies: - '@types/node': 22.19.15 - - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.35': - dependencies: - '@types/yargs-parser': 21.0.3 - - '@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.57.2(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.57.2 - '@typescript-eslint/type-utils': 8.57.2(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/utils': 8.57.2(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.57.2 - eslint: 9.39.4 - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.57.2 - '@typescript-eslint/types': 8.57.2 - '@typescript-eslint/typescript-estree': 8.57.2(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.57.2 - debug: 4.4.3 - eslint: 9.39.4 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.57.2(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.57.2(typescript@5.9.3) - '@typescript-eslint/types': 8.57.2 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.57.2': - dependencies: - '@typescript-eslint/types': 8.57.2 - '@typescript-eslint/visitor-keys': 8.57.2 - - '@typescript-eslint/tsconfig-utils@8.57.2(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - - '@typescript-eslint/type-utils@8.57.2(eslint@9.39.4)(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.57.2 - '@typescript-eslint/typescript-estree': 8.57.2(typescript@5.9.3) - '@typescript-eslint/utils': 8.57.2(eslint@9.39.4)(typescript@5.9.3) - debug: 4.4.3 - eslint: 9.39.4 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@8.57.2': {} - - '@typescript-eslint/typescript-estree@8.57.2(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.57.2(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.57.2(typescript@5.9.3) - '@typescript-eslint/types': 8.57.2 - '@typescript-eslint/visitor-keys': 8.57.2 - debug: 4.4.3 - minimatch: 10.2.4 - semver: 7.7.4 - tinyglobby: 0.2.15 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.57.2(eslint@9.39.4)(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.57.2 - '@typescript-eslint/types': 8.57.2 - '@typescript-eslint/typescript-estree': 8.57.2(typescript@5.9.3) - eslint: 9.39.4 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.57.2': - dependencies: - '@typescript-eslint/types': 8.57.2 - eslint-visitor-keys: 5.0.1 - - '@ungap/structured-clone@1.3.0': {} - - '@unrs/resolver-binding-android-arm-eabi@1.11.1': - optional: true - - '@unrs/resolver-binding-android-arm64@1.11.1': - optional: true - - '@unrs/resolver-binding-darwin-arm64@1.11.1': - optional: true - - '@unrs/resolver-binding-darwin-x64@1.11.1': - optional: true - - '@unrs/resolver-binding-freebsd-x64@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-arm64-musl@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-x64-gnu@1.11.1': - optional: true - - '@unrs/resolver-binding-linux-x64-musl@1.11.1': - optional: true - - '@unrs/resolver-binding-wasm32-wasi@1.11.1': - dependencies: - '@napi-rs/wasm-runtime': 0.2.12 - optional: true - - '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': - optional: true - - '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': - optional: true - - '@unrs/resolver-binding-win32-x64-msvc@1.11.1': - optional: true - - '@webassemblyjs/ast@1.14.1': - dependencies: - '@webassemblyjs/helper-numbers': 1.13.2 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - - '@webassemblyjs/floating-point-hex-parser@1.13.2': {} - - '@webassemblyjs/helper-api-error@1.13.2': {} - - '@webassemblyjs/helper-buffer@1.14.1': {} - - '@webassemblyjs/helper-numbers@1.13.2': - dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.13.2 - '@webassemblyjs/helper-api-error': 1.13.2 - '@xtuc/long': 4.2.2 - - '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} - - '@webassemblyjs/helper-wasm-section@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/wasm-gen': 1.14.1 - - '@webassemblyjs/ieee754@1.13.2': - dependencies: - '@xtuc/ieee754': 1.2.0 - - '@webassemblyjs/leb128@1.13.2': - dependencies: - '@xtuc/long': 4.2.2 - - '@webassemblyjs/utf8@1.13.2': {} - - '@webassemblyjs/wasm-edit@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/helper-wasm-section': 1.14.1 - '@webassemblyjs/wasm-gen': 1.14.1 - '@webassemblyjs/wasm-opt': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - '@webassemblyjs/wast-printer': 1.14.1 - - '@webassemblyjs/wasm-gen@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/ieee754': 1.13.2 - '@webassemblyjs/leb128': 1.13.2 - '@webassemblyjs/utf8': 1.13.2 - - '@webassemblyjs/wasm-opt@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/wasm-gen': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - - '@webassemblyjs/wasm-parser@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-api-error': 1.13.2 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/ieee754': 1.13.2 - '@webassemblyjs/leb128': 1.13.2 - '@webassemblyjs/utf8': 1.13.2 - - '@webassemblyjs/wast-printer@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@xtuc/long': 4.2.2 - - '@xtuc/ieee754@1.2.0': {} - - '@xtuc/long@4.2.2': {} - - abbrev@1.1.1: - optional: true - - accepts@1.3.8: - dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 - - accepts@2.0.0: - dependencies: - mime-types: 3.0.2 - negotiator: 1.0.0 - - acorn-import-phases@1.0.4(acorn@8.16.0): - dependencies: - acorn: 8.16.0 - - acorn-jsx@5.3.2(acorn@8.16.0): - dependencies: - acorn: 8.16.0 - - acorn-walk@8.3.5: - dependencies: - acorn: 8.16.0 - - acorn@8.16.0: {} - - agent-base@6.0.2: - dependencies: - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - optional: true - - agentkeepalive@4.6.0: - dependencies: - humanize-ms: 1.2.1 - optional: true - - aggregate-error@3.1.0: - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - optional: true - - ajv-formats@2.1.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 - - ajv-formats@3.0.1(ajv@8.17.1): - optionalDependencies: - ajv: 8.17.1 - - ajv-keywords@3.5.2(ajv@6.14.0): - dependencies: - ajv: 6.14.0 - - ajv-keywords@5.1.0(ajv@8.18.0): - dependencies: - ajv: 8.18.0 - fast-deep-equal: 3.1.3 - - ajv@6.14.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ajv@8.18.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-colors@4.1.3: {} - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - - ansi-regex@5.0.1: {} - - ansi-regex@6.2.2: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@5.2.0: {} - - ansi-styles@6.2.3: {} - - ansis@4.2.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.2 - - app-root-path@3.1.0: {} - - append-field@1.0.0: {} - - aproba@2.1.0: - optional: true - - are-we-there-yet@3.0.1: - dependencies: - delegates: 1.0.0 - readable-stream: 3.6.2 - optional: true - - arg@4.1.3: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - array-timsort@1.0.3: {} - - asap@2.0.6: {} - - asynckit@0.4.0: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - - axios@1.13.6: - dependencies: - follow-redirects: 1.15.11 - form-data: 4.0.5 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - - babel-jest@30.3.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - '@jest/transform': 30.3.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.3.0(@babel/core@7.29.0) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@7.0.1: - dependencies: - '@babel/helper-plugin-utils': 7.28.6 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 6.0.3 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-jest-hoist@30.3.0: - dependencies: - '@types/babel__core': 7.20.5 - - babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) - - babel-preset-jest@30.3.0(@babel/core@7.29.0): - dependencies: - '@babel/core': 7.29.0 - babel-plugin-jest-hoist: 30.3.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) - - balanced-match@1.0.2: {} - - balanced-match@4.0.4: {} - - bare-addon-resolve@1.10.0: - dependencies: - bare-module-resolve: 1.12.1 - bare-semver: 1.0.2 - optional: true - - bare-module-resolve@1.12.1: - dependencies: - bare-semver: 1.0.2 - optional: true - - bare-semver@1.0.2: - optional: true - - base32.js@0.1.0: {} - - base64-js@1.5.1: {} - - base64id@2.0.0: {} - - baseline-browser-mapping@2.10.10: {} - - bcrypt@6.0.0: - dependencies: - node-addon-api: 8.6.0 - node-gyp-build: 4.8.4 - - bignumber.js@9.3.1: {} - - bindings@1.5.0: - dependencies: - file-uri-to-path: 1.0.0 - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - body-parser@2.2.2: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.3 - http-errors: 2.0.1 - iconv-lite: 0.7.2 - on-finished: 2.4.1 - qs: 6.15.0 - raw-body: 3.0.2 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - brace-expansion@5.0.5: - dependencies: - balanced-match: 4.0.4 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.28.1: - dependencies: - baseline-browser-mapping: 2.10.10 - caniuse-lite: 1.0.30001781 - electron-to-chromium: 1.5.325 - node-releases: 2.0.36 - update-browserslist-db: 1.2.3(browserslist@4.28.1) - - bs-logger@0.2.6: - dependencies: - fast-json-stable-stringify: 2.1.0 - - bser@2.1.1: - dependencies: - node-int64: 0.4.0 - - buffer-equal-constant-time@1.0.1: {} - - buffer-from@1.1.2: {} - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - busboy@1.6.0: - dependencies: - streamsearch: 1.1.0 - - bytes@3.1.2: {} - - cacache@15.3.0: - dependencies: - '@npmcli/fs': 1.1.1 - '@npmcli/move-file': 1.1.2 - chownr: 2.0.0 - fs-minipass: 2.1.0 - glob: 7.2.3 - infer-owner: 1.0.4 - lru-cache: 6.0.0 - minipass: 3.3.6 - minipass-collect: 1.0.2 - minipass-flush: 1.0.6 - minipass-pipeline: 1.2.4 - mkdirp: 1.0.4 - p-map: 4.0.0 - promise-inflight: 1.0.1 - rimraf: 3.0.2 - ssri: 8.0.1 - tar: 6.2.1 - unique-filename: 1.1.1 - transitivePeerDependencies: - - bluebird - optional: true - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - callsites@3.1.0: {} - - camelcase@5.3.1: {} - - camelcase@6.3.0: {} - - caniuse-lite@1.0.30001781: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - char-regex@1.0.2: {} - - chardet@2.1.1: {} - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - chownr@1.1.4: {} - - chownr@2.0.0: {} - - chrome-trace-event@1.0.4: {} - - ci-info@4.4.0: {} - - cjs-module-lexer@2.2.0: {} - - class-transformer@0.5.1: {} - - class-validator@0.14.4: - dependencies: - '@types/validator': 13.15.10 - libphonenumber-js: 1.12.40 - validator: 13.15.26 - - clean-stack@2.2.0: - optional: true - - cli-cursor@3.1.0: - dependencies: - restore-cursor: 3.1.0 - - cli-spinners@2.9.2: {} - - cli-table3@0.6.5: - dependencies: - string-width: 4.2.3 - optionalDependencies: - '@colors/colors': 1.5.0 - - cli-width@4.1.0: {} - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clone@1.0.4: {} - - co@4.6.0: {} - - collect-v8-coverage@1.0.3: {} - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - color-support@1.1.3: - optional: true - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - commander@14.0.3: {} - - commander@2.20.3: {} - - commander@4.1.1: {} - - comment-json@4.4.1: - dependencies: - array-timsort: 1.0.3 - core-util-is: 1.0.3 - esprima: 4.0.1 - - component-emitter@1.3.1: {} - - concat-map@0.0.1: {} - - concat-stream@2.0.0: - dependencies: - buffer-from: 1.1.2 - inherits: 2.0.4 - readable-stream: 3.6.2 - typedarray: 0.0.6 - - consola@3.4.2: {} - - console-control-strings@1.1.0: - optional: true - - content-disposition@1.0.1: {} - - content-type@1.0.5: {} - - convert-source-map@2.0.0: {} - - cookie-signature@1.2.2: {} - - cookie@0.7.2: {} - - cookiejar@2.1.4: {} - - core-util-is@1.0.3: {} - - cors@2.8.6: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - - cosmiconfig@8.3.6(typescript@5.9.3): - dependencies: - import-fresh: 3.3.1 - js-yaml: 4.1.1 - parse-json: 5.2.0 - path-type: 4.0.0 - optionalDependencies: - typescript: 5.9.3 - - create-require@1.1.1: {} - - cron@4.4.0: - dependencies: - '@types/luxon': 3.7.1 - luxon: 3.7.2 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - dayjs@1.11.20: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - dedent@1.7.2: {} - - deep-extend@0.6.0: {} - - deep-is@0.1.4: {} - - deepmerge@4.3.1: {} - - defaults@1.0.4: - dependencies: - clone: 1.0.4 - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - delayed-stream@1.0.0: {} - - delegates@1.0.0: - optional: true - - depd@2.0.0: {} - - detect-libc@2.1.2: {} - - detect-newline@3.1.0: {} - - dezalgo@1.0.4: - dependencies: - asap: 2.0.6 - wrappy: 1.0.2 - - diff@4.0.4: {} - - dotenv-expand@12.0.3: - dependencies: - dotenv: 16.6.1 - - dotenv@16.6.1: {} - - dotenv@17.2.3: {} - - dotenv@17.3.1: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - eastasianwidth@0.2.0: {} - - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - - ee-first@1.1.1: {} - - electron-to-chromium@1.5.325: {} - - emittery@0.13.1: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - - encodeurl@2.0.0: {} - - encoding@0.1.13: - dependencies: - iconv-lite: 0.6.3 - optional: true - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - engine.io-parser@5.2.3: {} - - engine.io@6.6.7: - dependencies: - '@types/cors': 2.8.19 - '@types/node': 22.19.15 - '@types/ws': 8.18.1 - accepts: 1.3.8 - base64id: 2.0.0 - cookie: 0.7.2 - cors: 2.8.6 - debug: 4.4.3 - engine.io-parser: 5.2.3 - ws: 8.18.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - enhanced-resolve@5.20.1: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.2 - - env-paths@2.2.1: - optional: true - - err-code@2.0.3: - optional: true - - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-module-lexer@2.0.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - escalade@3.2.0: {} - - escape-html@1.0.3: {} - - escape-string-regexp@2.0.0: {} - - escape-string-regexp@4.0.0: {} - - eslint-config-prettier@10.1.8(eslint@9.39.4): - dependencies: - eslint: 9.39.4 - - eslint-plugin-prettier@5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.4))(eslint@9.39.4)(prettier@3.8.1): - dependencies: - eslint: 9.39.4 - prettier: 3.8.1 - prettier-linter-helpers: 1.0.1 - synckit: 0.11.12 - optionalDependencies: - '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.8(eslint@9.39.4) - - eslint-scope@5.1.1: - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - - eslint-scope@8.4.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.1: {} - - eslint-visitor-keys@5.0.1: {} - - eslint@9.39.4: - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.2 - '@eslint/config-helpers': 0.4.2 - '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.5 - '@eslint/js': 9.39.4 - '@eslint/plugin-kit': 0.4.1 - '@humanfs/node': 0.16.7 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.14.0 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.7.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.5 - natural-compare: 1.4.0 - optionator: 0.9.4 - transitivePeerDependencies: - - supports-color - - espree@10.4.0: - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - eslint-visitor-keys: 4.2.1 - - esprima@4.0.1: {} - - esquery@1.7.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@4.3.0: {} - - estraverse@5.3.0: {} - - esutils@2.0.3: {} - - etag@1.8.1: {} - - events@3.3.0: {} - - eventsource@2.0.2: {} - - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - exit-x@0.2.2: {} - - expand-template@2.0.3: {} - - expect@30.3.0: - dependencies: - '@jest/expect-utils': 30.3.0 - '@jest/get-type': 30.1.0 - jest-matcher-utils: 30.3.0 - jest-message-util: 30.3.0 - jest-mock: 30.3.0 - jest-util: 30.3.0 - - express@5.2.1: - dependencies: - accepts: 2.0.0 - body-parser: 2.2.2 - content-disposition: 1.0.1 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.1 - fresh: 2.0.0 - http-errors: 2.0.1 - merge-descriptors: 2.0.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.15.0 - range-parser: 1.2.1 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 - statuses: 2.0.2 - type-is: 2.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - - fast-deep-equal@3.1.3: {} - - fast-diff@1.3.0: {} - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fast-safe-stringify@2.1.1: {} - - fast-uri@3.1.0: {} - - fb-watchman@2.0.2: - dependencies: - bser: 2.1.1 - - fdir@6.5.0(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - - feaxios@0.0.23: - dependencies: - is-retry-allowed: 3.0.0 - - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - - file-type@21.3.2: - dependencies: - '@tokenizer/inflate': 0.4.1 - strtok3: 10.3.5 - token-types: 6.1.2 - uint8array-extras: 1.5.0 - transitivePeerDependencies: - - supports-color - - file-uri-to-path@1.0.0: {} - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - finalhandler@2.1.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@4.0.1: - dependencies: - flatted: 3.4.2 - keyv: 4.5.4 - - flatted@3.4.2: {} - - follow-redirects@1.15.11: {} - - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.104.1): - dependencies: - '@babel/code-frame': 7.29.0 - chalk: 4.1.2 - chokidar: 4.0.3 - cosmiconfig: 8.3.6(typescript@5.9.3) - deepmerge: 4.3.1 - fs-extra: 10.1.0 - memfs: 3.5.3 - minimatch: 3.1.5 - node-abort-controller: 3.1.1 - schema-utils: 3.3.0 - semver: 7.7.4 - tapable: 2.3.2 - typescript: 5.9.3 - webpack: 5.104.1 - - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - - formidable@3.5.4: - dependencies: - '@paralleldrive/cuid2': 2.3.1 - dezalgo: 1.0.4 - once: 1.4.0 - - forwarded@0.2.0: {} - - fresh@2.0.0: {} - - fs-constants@1.0.0: {} - - fs-extra@10.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - - fs-monkey@1.1.0: {} - - fs.realpath@1.0.0: {} - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - gauge@4.0.4: - dependencies: - aproba: 2.1.0 - color-support: 1.1.3 - console-control-strings: 1.1.0 - has-unicode: 2.0.1 - signal-exit: 3.0.7 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wide-align: 1.1.5 - optional: true - - gensync@1.0.0-beta.2: {} - - get-caller-file@2.0.5: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-package-type@0.1.0: {} - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-stream@6.0.1: {} - - github-from-package@0.0.0: {} - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - glob-to-regexp@0.4.1: {} - - glob@10.5.0: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.9 - minipass: 7.1.3 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - - glob@13.0.0: - dependencies: - minimatch: 10.2.4 - minipass: 7.1.3 - path-scurry: 2.0.2 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.5 - once: 1.4.0 - path-is-absolute: 1.0.1 - - globals@14.0.0: {} - - globals@16.5.0: {} - - gopd@1.2.0: {} - - graceful-fs@4.2.11: {} - - handlebars@4.7.8: - dependencies: - minimist: 1.2.8 - neo-async: 2.6.2 - source-map: 0.6.1 - wordwrap: 1.0.0 - optionalDependencies: - uglify-js: 3.19.3 - - has-flag@4.0.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - has-unicode@2.0.1: - optional: true - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - html-escaper@2.0.2: {} - - http-cache-semantics@4.2.0: - optional: true - - 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 - - http-proxy-agent@4.0.1: - dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - optional: true - - https-proxy-agent@5.0.1: - dependencies: - agent-base: 6.0.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - optional: true - - human-signals@2.1.0: {} - - humanize-ms@1.2.1: - dependencies: - ms: 2.1.3 - optional: true - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - optional: true - - iconv-lite@0.7.2: - dependencies: - safer-buffer: 2.1.2 - - ieee754@1.2.1: {} - - ignore@5.3.2: {} - - ignore@7.0.5: {} - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - import-local@3.2.0: - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - - imurmurhash@0.1.4: {} - - indent-string@4.0.0: - optional: true - - infer-owner@1.0.4: - optional: true - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - ini@1.3.8: {} - - ip-address@10.1.0: - optional: true - - ipaddr.js@1.9.1: {} - - is-arrayish@0.2.1: {} - - is-callable@1.2.7: {} - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@3.0.0: {} - - is-generator-fn@2.1.0: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-interactive@1.0.0: {} - - is-lambda@1.0.1: - optional: true - - is-number@7.0.0: {} - - is-promise@4.0.0: {} - - is-retry-allowed@3.0.0: {} - - is-stream@2.0.1: {} - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.20 - - is-unicode-supported@0.1.0: {} - - isarray@2.0.5: {} - - isexe@2.0.0: {} - - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-instrument@6.0.3: - dependencies: - '@babel/core': 7.29.0 - '@babel/parser': 7.29.2 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 7.7.4 - transitivePeerDependencies: - - supports-color - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - debug: 4.4.3 - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.2.0: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - iterare@1.2.1: {} - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jest-changed-files@30.3.0: - dependencies: - execa: 5.1.1 - jest-util: 30.3.0 - p-limit: 3.1.0 - - jest-circus@30.3.0: - dependencies: - '@jest/environment': 30.3.0 - '@jest/expect': 30.3.0 - '@jest/test-result': 30.3.0 - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.7.2 - is-generator-fn: 2.1.0 - jest-each: 30.3.0 - jest-matcher-utils: 30.3.0 - jest-message-util: 30.3.0 - jest-runtime: 30.3.0 - jest-snapshot: 30.3.0 - jest-util: 30.3.0 - p-limit: 3.1.0 - pretty-format: 30.3.0 - pure-rand: 7.0.1 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-cli@30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)): - dependencies: - '@jest/core': 30.3.0(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)) - '@jest/test-result': 30.3.0 - '@jest/types': 30.3.0 - chalk: 4.1.2 - exit-x: 0.2.2 - import-local: 3.2.0 - jest-config: 30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)) - jest-util: 30.3.0 - jest-validate: 30.3.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - esbuild-register - - supports-color - - ts-node - - jest-config@30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)): - dependencies: - '@babel/core': 7.29.0 - '@jest/get-type': 30.1.0 - '@jest/pattern': 30.0.1 - '@jest/test-sequencer': 30.3.0 - '@jest/types': 30.3.0 - babel-jest: 30.3.0(@babel/core@7.29.0) - chalk: 4.1.2 - ci-info: 4.4.0 - deepmerge: 4.3.1 - glob: 10.5.0 - graceful-fs: 4.2.11 - jest-circus: 30.3.0 - jest-docblock: 30.2.0 - jest-environment-node: 30.3.0 - jest-regex-util: 30.0.1 - jest-resolve: 30.3.0 - jest-runner: 30.3.0 - jest-util: 30.3.0 - jest-validate: 30.3.0 - parse-json: 5.2.0 - pretty-format: 30.3.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.19.15 - ts-node: 10.9.2(@types/node@22.19.15)(typescript@5.9.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-diff@30.3.0: - dependencies: - '@jest/diff-sequences': 30.3.0 - '@jest/get-type': 30.1.0 - chalk: 4.1.2 - pretty-format: 30.3.0 - - jest-docblock@30.2.0: - dependencies: - detect-newline: 3.1.0 - - jest-each@30.3.0: - dependencies: - '@jest/get-type': 30.1.0 - '@jest/types': 30.3.0 - chalk: 4.1.2 - jest-util: 30.3.0 - pretty-format: 30.3.0 - - jest-environment-node@30.3.0: - dependencies: - '@jest/environment': 30.3.0 - '@jest/fake-timers': 30.3.0 - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - jest-mock: 30.3.0 - jest-util: 30.3.0 - jest-validate: 30.3.0 - - jest-haste-map@30.3.0: - dependencies: - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 30.0.1 - jest-util: 30.3.0 - jest-worker: 30.3.0 - picomatch: 4.0.4 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - - jest-leak-detector@30.3.0: - dependencies: - '@jest/get-type': 30.1.0 - pretty-format: 30.3.0 - - jest-matcher-utils@30.3.0: - dependencies: - '@jest/get-type': 30.1.0 - chalk: 4.1.2 - jest-diff: 30.3.0 - pretty-format: 30.3.0 - - jest-message-util@30.3.0: - dependencies: - '@babel/code-frame': 7.29.0 - '@jest/types': 30.3.0 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - picomatch: 4.0.4 - pretty-format: 30.3.0 - slash: 3.0.0 - stack-utils: 2.0.6 - - jest-mock@30.3.0: - dependencies: - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - jest-util: 30.3.0 - - jest-pnp-resolver@1.2.3(jest-resolve@30.3.0): - optionalDependencies: - jest-resolve: 30.3.0 - - jest-regex-util@30.0.1: {} - - jest-resolve-dependencies@30.3.0: - dependencies: - jest-regex-util: 30.0.1 - jest-snapshot: 30.3.0 - transitivePeerDependencies: - - supports-color - - jest-resolve@30.3.0: - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 30.3.0 - jest-pnp-resolver: 1.2.3(jest-resolve@30.3.0) - jest-util: 30.3.0 - jest-validate: 30.3.0 - slash: 3.0.0 - unrs-resolver: 1.11.1 - - jest-runner@30.3.0: - dependencies: - '@jest/console': 30.3.0 - '@jest/environment': 30.3.0 - '@jest/test-result': 30.3.0 - '@jest/transform': 30.3.0 - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - chalk: 4.1.2 - emittery: 0.13.1 - exit-x: 0.2.2 - graceful-fs: 4.2.11 - jest-docblock: 30.2.0 - jest-environment-node: 30.3.0 - jest-haste-map: 30.3.0 - jest-leak-detector: 30.3.0 - jest-message-util: 30.3.0 - jest-resolve: 30.3.0 - jest-runtime: 30.3.0 - jest-util: 30.3.0 - jest-watcher: 30.3.0 - jest-worker: 30.3.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - - jest-runtime@30.3.0: - dependencies: - '@jest/environment': 30.3.0 - '@jest/fake-timers': 30.3.0 - '@jest/globals': 30.3.0 - '@jest/source-map': 30.0.1 - '@jest/test-result': 30.3.0 - '@jest/transform': 30.3.0 - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - chalk: 4.1.2 - cjs-module-lexer: 2.2.0 - collect-v8-coverage: 1.0.3 - glob: 10.5.0 - graceful-fs: 4.2.11 - jest-haste-map: 30.3.0 - jest-message-util: 30.3.0 - jest-mock: 30.3.0 - jest-regex-util: 30.0.1 - jest-resolve: 30.3.0 - jest-snapshot: 30.3.0 - jest-util: 30.3.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - - jest-snapshot@30.3.0: - dependencies: - '@babel/core': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) - '@babel/types': 7.29.0 - '@jest/expect-utils': 30.3.0 - '@jest/get-type': 30.1.0 - '@jest/snapshot-utils': 30.3.0 - '@jest/transform': 30.3.0 - '@jest/types': 30.3.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) - chalk: 4.1.2 - expect: 30.3.0 - graceful-fs: 4.2.11 - jest-diff: 30.3.0 - jest-matcher-utils: 30.3.0 - jest-message-util: 30.3.0 - jest-util: 30.3.0 - pretty-format: 30.3.0 - semver: 7.7.4 - synckit: 0.11.12 - transitivePeerDependencies: - - supports-color - - jest-util@30.3.0: - dependencies: - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - chalk: 4.1.2 - ci-info: 4.4.0 - graceful-fs: 4.2.11 - picomatch: 4.0.4 - - jest-validate@30.3.0: - dependencies: - '@jest/get-type': 30.1.0 - '@jest/types': 30.3.0 - camelcase: 6.3.0 - chalk: 4.1.2 - leven: 3.1.0 - pretty-format: 30.3.0 - - jest-watcher@30.3.0: - dependencies: - '@jest/test-result': 30.3.0 - '@jest/types': 30.3.0 - '@types/node': 22.19.15 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 30.3.0 - string-length: 4.0.2 - - jest-worker@27.5.1: - dependencies: - '@types/node': 22.19.15 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - jest-worker@30.3.0: - dependencies: - '@types/node': 22.19.15 - '@ungap/structured-clone': 1.3.0 - jest-util: 30.3.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - jest@30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)): - dependencies: - '@jest/core': 30.3.0(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)) - '@jest/types': 30.3.0 - import-local: 3.2.0 - jest-cli: 30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - esbuild-register - - supports-color - - ts-node - - js-tokens@4.0.0: {} - - js-yaml@3.14.2: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - js-yaml@4.1.1: - dependencies: - argparse: 2.0.1 - - jsesc@3.1.0: {} - - json-buffer@3.0.1: {} - - json-parse-even-better-errors@2.3.1: {} - - json-schema-traverse@0.4.1: {} - - json-schema-traverse@1.0.0: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@2.2.3: {} - - jsonc-parser@3.3.1: {} - - jsonfile@6.2.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - - jsonwebtoken@9.0.3: - dependencies: - jws: 4.0.1 - lodash.includes: 4.3.0 - lodash.isboolean: 3.0.3 - lodash.isinteger: 4.0.4 - lodash.isnumber: 3.0.3 - lodash.isplainobject: 4.0.6 - lodash.isstring: 4.0.1 - lodash.once: 4.1.1 - ms: 2.1.3 - semver: 7.7.4 - - jwa@2.0.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.1: - dependencies: - jwa: 2.0.1 - safe-buffer: 5.2.1 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - leven@3.1.0: {} - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - libphonenumber-js@1.12.40: {} - - lines-and-columns@1.2.4: {} - - load-esm@1.0.3: {} - - loader-runner@4.3.1: {} - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - lodash.includes@4.3.0: {} - - lodash.isboolean@3.0.3: {} - - lodash.isinteger@4.0.4: {} - - lodash.isnumber@3.0.3: {} - - lodash.isplainobject@4.0.6: {} - - lodash.isstring@4.0.1: {} - - lodash.memoize@4.1.2: {} - - lodash.merge@4.6.2: {} - - lodash.once@4.1.1: {} - - lodash@4.17.23: {} - - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - - lru-cache@10.4.3: {} - - lru-cache@11.2.7: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - optional: true - - luxon@3.7.2: {} - - magic-string@0.30.17: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - make-dir@4.0.0: - dependencies: - semver: 7.7.4 - - make-error@1.3.6: {} - - make-fetch-happen@9.1.0: - dependencies: - agentkeepalive: 4.6.0 - cacache: 15.3.0 - http-cache-semantics: 4.2.0 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1 - is-lambda: 1.0.1 - lru-cache: 6.0.0 - minipass: 3.3.6 - minipass-collect: 1.0.2 - minipass-fetch: 1.4.1 - minipass-flush: 1.0.6 - minipass-pipeline: 1.2.4 - negotiator: 0.6.4 - promise-retry: 2.0.1 - socks-proxy-agent: 6.2.1 - ssri: 8.0.1 - transitivePeerDependencies: - - bluebird - - supports-color - optional: true - - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - - math-intrinsics@1.1.0: {} - - media-typer@0.3.0: {} - - media-typer@1.1.0: {} - - memfs@3.5.3: - dependencies: - fs-monkey: 1.1.0 - - merge-descriptors@2.0.0: {} - - merge-stream@2.0.0: {} - - methods@1.1.2: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - - mime-db@1.52.0: {} - - mime-db@1.54.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mime-types@3.0.2: - dependencies: - mime-db: 1.54.0 - - mime@2.6.0: {} - - mimic-fn@2.1.0: {} - - mimic-response@3.1.0: {} - - minimatch@10.2.4: - dependencies: - brace-expansion: 5.0.5 - - minimatch@3.1.5: - dependencies: - brace-expansion: 1.1.12 - - minimatch@9.0.9: - dependencies: - brace-expansion: 2.0.2 - - minimist@1.2.8: {} - - minipass-collect@1.0.2: - dependencies: - minipass: 3.3.6 - optional: true - - minipass-fetch@1.4.1: - dependencies: - minipass: 3.3.6 - minipass-sized: 1.0.3 - minizlib: 2.1.2 - optionalDependencies: - encoding: 0.1.13 - optional: true - - minipass-flush@1.0.6: - dependencies: - minipass: 7.1.3 - optional: true - - minipass-pipeline@1.2.4: - dependencies: - minipass: 3.3.6 - optional: true - - minipass-sized@1.0.3: - dependencies: - minipass: 3.3.6 - optional: true - - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - - minipass@5.0.0: {} - - minipass@7.1.3: {} - - minizlib@2.1.2: - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - - mkdirp-classic@0.5.3: {} - - mkdirp@1.0.4: {} - - ms@2.1.3: {} - - multer@2.1.1: - dependencies: - append-field: 1.0.0 - busboy: 1.6.0 - concat-stream: 2.0.0 - type-is: 1.6.18 - - mute-stream@2.0.0: {} - - napi-build-utils@2.0.0: {} - - napi-postinstall@0.3.4: {} - - natural-compare@1.4.0: {} - - negotiator@0.6.3: {} - - negotiator@0.6.4: - optional: true - - negotiator@1.0.0: {} - - neo-async@2.6.2: {} - - node-abi@3.89.0: - dependencies: - semver: 7.7.4 - - node-abort-controller@3.1.1: {} - - node-addon-api@7.1.1: {} - - node-addon-api@8.6.0: {} - - node-emoji@1.11.0: - dependencies: - lodash: 4.17.23 - - node-gyp-build@4.8.4: {} - - node-gyp@8.4.1: - dependencies: - env-paths: 2.2.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - make-fetch-happen: 9.1.0 - nopt: 5.0.0 - npmlog: 6.0.2 - rimraf: 3.0.2 - semver: 7.7.4 - tar: 6.2.1 - which: 2.0.2 - transitivePeerDependencies: - - bluebird - - supports-color - optional: true - - node-int64@0.4.0: {} - - node-releases@2.0.36: {} - - nodemailer@8.0.4: {} - - nopt@5.0.0: - dependencies: - abbrev: 1.1.1 - optional: true - - normalize-path@3.0.0: {} - - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - - npmlog@6.0.2: - dependencies: - are-we-there-yet: 3.0.1 - console-control-strings: 1.1.0 - gauge: 4.0.4 - set-blocking: 2.0.0 - optional: true - - object-assign@4.1.1: {} - - object-hash@3.0.0: {} - - object-inspect@1.13.4: {} - - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - ora@5.4.1: - dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.9.2 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - p-map@4.0.0: - dependencies: - aggregate-error: 3.1.0 - optional: true - - p-try@2.2.0: {} - - package-json-from-dist@1.0.1: {} - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.29.0 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - parseurl@1.3.3: {} - - passport-jwt@4.0.1: - dependencies: - jsonwebtoken: 9.0.3 - passport-strategy: 1.0.0 - - passport-strategy@1.0.0: {} - - passport@0.7.0: - dependencies: - passport-strategy: 1.0.0 - pause: 0.0.1 - utils-merge: 1.0.1 - - path-exists@4.0.0: {} - - path-is-absolute@1.0.1: {} - - path-key@3.1.1: {} - - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.3 - - path-scurry@2.0.2: - dependencies: - lru-cache: 11.2.7 - minipass: 7.1.3 - - path-to-regexp@8.3.0: {} - - path-type@4.0.0: {} - - pause@0.0.1: {} - - picocolors@1.1.1: {} - - picomatch@2.3.2: {} - - picomatch@4.0.2: {} - - picomatch@4.0.4: {} - - pirates@4.0.7: {} - - pkg-dir@4.2.0: - dependencies: - find-up: 4.1.0 - - pluralize@8.0.0: {} - - possible-typed-array-names@1.1.0: {} - - prebuild-install@7.1.3: - dependencies: - detect-libc: 2.1.2 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 2.0.0 - node-abi: 3.89.0 - pump: 3.0.4 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.4 - tunnel-agent: 0.6.0 - - prelude-ls@1.2.1: {} - - prettier-linter-helpers@1.0.1: - dependencies: - fast-diff: 1.3.0 - - prettier@3.8.1: {} - - pretty-format@30.3.0: - dependencies: - '@jest/schemas': 30.0.5 - ansi-styles: 5.2.0 - react-is: 18.3.1 - - promise-inflight@1.0.1: - optional: true - - promise-retry@2.0.1: - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - optional: true - - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - - proxy-from-env@1.1.0: {} - - pump@3.0.4: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - punycode@2.3.1: {} - - pure-rand@7.0.1: {} - - qs@6.15.0: - dependencies: - side-channel: 1.1.0 - - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - - range-parser@1.2.1: {} - - raw-body@3.0.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.7.2 - unpipe: 1.0.0 - - rc@1.2.8: - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - - react-is@18.3.1: {} - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - readdirp@4.1.2: {} - - reflect-metadata@0.2.2: {} - - require-addon@1.2.0: - dependencies: - bare-addon-resolve: 1.10.0 - transitivePeerDependencies: - - bare-url - optional: true - - require-directory@2.1.1: {} - - require-from-string@2.0.2: {} - - resolve-cwd@3.0.0: - dependencies: - resolve-from: 5.0.0 - - resolve-from@4.0.0: {} - - resolve-from@5.0.0: {} - - restore-cursor@3.1.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - - retry@0.12.0: - optional: true - - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - optional: true - - router@2.2.0: - dependencies: - debug: 4.4.3 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.3.0 - transitivePeerDependencies: - - supports-color - - rxjs@7.8.1: - dependencies: - tslib: 2.8.1 - - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - - safe-buffer@5.2.1: {} - - safer-buffer@2.1.2: {} - - schema-utils@3.3.0: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 6.14.0 - ajv-keywords: 3.5.2(ajv@6.14.0) - - schema-utils@4.3.3: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 8.18.0 - ajv-formats: 2.1.1(ajv@8.18.0) - ajv-keywords: 5.1.0(ajv@8.18.0) - - semver@6.3.1: {} - - semver@7.7.4: {} - - send@1.2.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.1 - mime-types: 3.0.2 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - - serve-static@2.2.1: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.1 - transitivePeerDependencies: - - supports-color - - set-blocking@2.0.0: - optional: true - - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - setprototypeof@1.2.0: {} - - sha.js@2.4.12: - dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - to-buffer: 1.2.2 - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - signal-exit@3.0.7: {} - - signal-exit@4.1.0: {} - - simple-concat@1.0.1: {} - - simple-get@4.0.1: - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - - slash@3.0.0: {} - - smart-buffer@4.2.0: - optional: true - - socket.io-adapter@2.5.6: - dependencies: - debug: 4.4.3 - ws: 8.18.3 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - socket.io-parser@4.2.6: - dependencies: - '@socket.io/component-emitter': 3.1.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - socket.io@4.8.3: - dependencies: - accepts: 1.3.8 - base64id: 2.0.0 - cors: 2.8.6 - debug: 4.4.3 - engine.io: 6.6.7 - socket.io-adapter: 2.5.6 - socket.io-parser: 4.2.6 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - socks-proxy-agent@6.2.1: - dependencies: - agent-base: 6.0.2 - debug: 4.4.3 - socks: 2.8.7 - transitivePeerDependencies: - - supports-color - optional: true - - socks@2.8.7: - dependencies: - ip-address: 10.1.0 - smart-buffer: 4.2.0 - optional: true - - sodium-native@4.3.3: - dependencies: - require-addon: 1.2.0 - transitivePeerDependencies: - - bare-url - optional: true - - source-map-support@0.5.13: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map@0.6.1: {} - - source-map@0.7.4: {} - - source-map@0.7.6: {} - - sprintf-js@1.0.3: {} - - sql-highlight@6.1.0: {} - - sqlite3@5.1.7: - dependencies: - bindings: 1.5.0 - node-addon-api: 7.1.1 - prebuild-install: 7.1.3 - tar: 6.2.1 - optionalDependencies: - node-gyp: 8.4.1 - transitivePeerDependencies: - - bluebird - - supports-color - - ssri@8.0.1: - dependencies: - minipass: 3.3.6 - optional: true - - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - - statuses@2.0.2: {} - - stellar-sdk@13.3.0: - dependencies: - '@stellar/stellar-base': 13.1.0 - axios: 1.13.6 - bignumber.js: 9.3.1 - eventsource: 2.0.2 - feaxios: 0.0.23 - randombytes: 2.1.0 - toml: 3.0.0 - urijs: 1.19.11 - transitivePeerDependencies: - - bare-url - - debug - - streamsearch@1.1.0: {} - - string-length@4.0.2: - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.2.0 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.2.0: - dependencies: - ansi-regex: 6.2.2 - - strip-bom@3.0.0: {} - - strip-bom@4.0.0: {} - - strip-final-newline@2.0.0: {} - - strip-json-comments@2.0.1: {} - - strip-json-comments@3.1.1: {} - - strtok3@10.3.5: - dependencies: - '@tokenizer/token': 0.3.0 - - superagent@10.3.0: - dependencies: - component-emitter: 1.3.1 - cookiejar: 2.1.4 - debug: 4.4.3 - fast-safe-stringify: 2.1.1 - form-data: 4.0.5 - formidable: 3.5.4 - methods: 1.1.2 - mime: 2.6.0 - qs: 6.15.0 - transitivePeerDependencies: - - supports-color - - supertest@7.2.2: - dependencies: - cookie-signature: 1.2.2 - methods: 1.1.2 - superagent: 10.3.0 - transitivePeerDependencies: - - supports-color - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - - swagger-ui-dist@5.31.0: - dependencies: - '@scarf/scarf': 1.4.0 - - swagger-ui-dist@5.32.1: - dependencies: - '@scarf/scarf': 1.4.0 - - swagger-ui-express@5.0.1(express@5.2.1): - dependencies: - express: 5.2.1 - swagger-ui-dist: 5.32.1 - - symbol-observable@4.0.0: {} - - synckit@0.11.12: - dependencies: - '@pkgr/core': 0.2.9 - - tapable@2.3.2: {} - - tar-fs@2.1.4: - dependencies: - chownr: 1.1.4 - mkdirp-classic: 0.5.3 - pump: 3.0.4 - tar-stream: 2.2.0 - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.5 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - - tar@6.2.1: - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - - terser-webpack-plugin@5.4.0(webpack@5.104.1): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - jest-worker: 27.5.1 - schema-utils: 4.3.3 - terser: 5.46.1 - webpack: 5.104.1 - - terser@5.46.1: - dependencies: - '@jridgewell/source-map': 0.3.11 - acorn: 8.16.0 - commander: 2.20.3 - source-map-support: 0.5.21 - - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.5 - - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - - tmpl@1.0.5: {} - - to-buffer@1.2.2: - dependencies: - isarray: 2.0.5 - safe-buffer: 5.2.1 - typed-array-buffer: 1.0.3 - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - toidentifier@1.0.1: {} - - token-types@6.1.2: - dependencies: - '@borewit/text-codec': 0.2.2 - '@tokenizer/token': 0.3.0 - ieee754: 1.2.1 - - toml@3.0.0: {} - - ts-api-utils@2.5.0(typescript@5.9.3): - dependencies: - typescript: 5.9.3 - - ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)))(typescript@5.9.3): - dependencies: - bs-logger: 0.2.6 - fast-json-stable-stringify: 2.1.0 - handlebars: 4.7.8 - jest: 30.3.0(@types/node@22.19.15)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)) - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.7.4 - type-fest: 4.41.0 - typescript: 5.9.3 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.29.0 - '@jest/transform': 30.3.0 - '@jest/types': 30.3.0 - babel-jest: 30.3.0(@babel/core@7.29.0) - jest-util: 30.3.0 - - ts-loader@9.5.4(typescript@5.9.3)(webpack@5.104.1): - dependencies: - chalk: 4.1.2 - enhanced-resolve: 5.20.1 - micromatch: 4.0.8 - semver: 7.7.4 - source-map: 0.7.6 - typescript: 5.9.3 - webpack: 5.104.1 - - ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 22.19.15 - acorn: 8.16.0 - acorn-walk: 8.3.5 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.4 - make-error: 1.3.6 - typescript: 5.9.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - - tsconfig-paths-webpack-plugin@4.2.0: - dependencies: - chalk: 4.1.2 - enhanced-resolve: 5.20.1 - tapable: 2.3.2 - tsconfig-paths: 4.2.0 - - tsconfig-paths@4.2.0: - dependencies: - json5: 2.2.3 - minimist: 1.2.8 - strip-bom: 3.0.0 - - tslib@2.8.1: {} - - tunnel-agent@0.6.0: - dependencies: - safe-buffer: 5.2.1 - - tweetnacl@1.0.3: {} - - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-detect@4.0.8: {} - - type-fest@0.21.3: {} - - type-fest@4.41.0: {} - - type-is@1.6.18: - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - - type-is@2.0.1: - dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 - mime-types: 3.0.2 - - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - typedarray@0.0.6: {} - - typeorm@0.3.28(sqlite3@5.1.7)(ts-node@10.9.2(@types/node@22.19.15)(typescript@5.9.3)): - dependencies: - '@sqltools/formatter': 1.2.5 - ansis: 4.2.0 - app-root-path: 3.1.0 - buffer: 6.0.3 - dayjs: 1.11.20 - debug: 4.4.3 - dedent: 1.7.2 - dotenv: 16.6.1 - glob: 10.5.0 - reflect-metadata: 0.2.2 - sha.js: 2.4.12 - sql-highlight: 6.1.0 - tslib: 2.8.1 - uuid: 11.1.0 - yargs: 17.7.2 - optionalDependencies: - sqlite3: 5.1.7 - ts-node: 10.9.2(@types/node@22.19.15)(typescript@5.9.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - typescript-eslint@8.57.2(eslint@9.39.4)(typescript@5.9.3): - dependencies: - '@typescript-eslint/eslint-plugin': 8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/parser': 8.57.2(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.57.2(typescript@5.9.3) - '@typescript-eslint/utils': 8.57.2(eslint@9.39.4)(typescript@5.9.3) - eslint: 9.39.4 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - typescript@5.9.3: {} - - uglify-js@3.19.3: - optional: true - - uid@2.0.2: - dependencies: - '@lukeed/csprng': 1.1.0 - - uint8array-extras@1.5.0: {} - - undici-types@6.21.0: {} - - unique-filename@1.1.1: - dependencies: - unique-slug: 2.0.2 - optional: true - - unique-slug@2.0.2: - dependencies: - imurmurhash: 0.1.4 - optional: true - - universalify@2.0.1: {} - - unpipe@1.0.0: {} - - unrs-resolver@1.11.1: - dependencies: - napi-postinstall: 0.3.4 - optionalDependencies: - '@unrs/resolver-binding-android-arm-eabi': 1.11.1 - '@unrs/resolver-binding-android-arm64': 1.11.1 - '@unrs/resolver-binding-darwin-arm64': 1.11.1 - '@unrs/resolver-binding-darwin-x64': 1.11.1 - '@unrs/resolver-binding-freebsd-x64': 1.11.1 - '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 - '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 - '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 - '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 - '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 - '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-x64-musl': 1.11.1 - '@unrs/resolver-binding-wasm32-wasi': 1.11.1 - '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 - '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 - '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - - update-browserslist-db@1.2.3(browserslist@4.28.1): - dependencies: - browserslist: 4.28.1 - escalade: 3.2.0 - picocolors: 1.1.1 - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - urijs@1.19.11: {} - - util-deprecate@1.0.2: {} - - utils-merge@1.0.1: {} - - uuid@11.1.0: {} - - v8-compile-cache-lib@3.0.1: {} - - v8-to-istanbul@9.3.0: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - '@types/istanbul-lib-coverage': 2.0.6 - convert-source-map: 2.0.0 - - validator@13.15.26: {} - - vary@1.1.2: {} - - walker@1.0.8: - dependencies: - makeerror: 1.0.12 - - watchpack@2.5.1: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - - wcwidth@1.0.1: - dependencies: - defaults: 1.0.4 - - webpack-node-externals@3.0.0: {} - - webpack-sources@3.3.4: {} - - webpack@5.104.1: - dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/wasm-edit': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.16.0 - acorn-import-phases: 1.0.4(acorn@8.16.0) - browserslist: 4.28.1 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.20.1 - es-module-lexer: 2.0.0 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.1 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 4.3.3 - tapable: 2.3.2 - terser-webpack-plugin: 5.4.0(webpack@5.104.1) - watchpack: 2.5.1 - webpack-sources: 3.3.4 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - - which-typed-array@1.1.20: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - wide-align@1.1.5: - dependencies: - string-width: 4.2.3 - optional: true - - word-wrap@1.2.5: {} - - wordwrap@1.0.0: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 5.1.2 - strip-ansi: 7.2.0 - - wrappy@1.0.2: {} - - write-file-atomic@5.0.1: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 4.1.0 - - ws@8.18.3: {} - - y18n@5.0.8: {} - - yallist@3.1.1: {} - - yallist@4.0.0: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - yn@3.1.1: {} - - yocto-queue@0.1.0: {} - - yoctocolors-cjs@2.1.3: {} diff --git a/apps/backend/pnpm-workspace.yaml b/apps/backend/pnpm-workspace.yaml deleted file mode 100644 index 3093d525..00000000 --- a/apps/backend/pnpm-workspace.yaml +++ /dev/null @@ -1,6 +0,0 @@ -onlyBuiltDependencies: - - '@nestjs/core' - - '@scarf/scarf' - - bcrypt - - sqlite3 - - unrs-resolver diff --git a/apps/backend/src/api-key/api-key.controller.ts b/apps/backend/src/api-key/api-key.controller.ts deleted file mode 100644 index eba3e404..00000000 --- a/apps/backend/src/api-key/api-key.controller.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { - Controller, - Post, - Get, - Delete, - Body, - Param, - UseGuards, - Req, -} from '@nestjs/common'; -import { AuthGuard } from '../modules/auth/middleware/auth.guard'; -import { ApiKeysService } from './api-key.service'; -import { CreateApiKeyDto } from './dto/create-api-key.dto'; - -interface AuthenticatedRequest { - user: { - sub: string; - id?: string; - }; -} - -@Controller('api-keys') -@UseGuards(AuthGuard) -export class ApiKeyController { - constructor(private readonly apiKeysService: ApiKeysService) {} - - @Post() - async create(@Req() req: AuthenticatedRequest, @Body() dto: CreateApiKeyDto) { - const userId = req.user.sub; - return this.apiKeysService.create(userId, dto); - } - - @Get() - async list(@Req() req: AuthenticatedRequest) { - const userId = req.user.sub; - return this.apiKeysService.list(userId); - } - - @Delete(':id') - async revoke(@Req() req: AuthenticatedRequest, @Param('id') id: string) { - const userId = req.user.sub; - return this.apiKeysService.revoke(id, userId); - } -} diff --git a/apps/backend/src/api-key/api-key.module.ts b/apps/backend/src/api-key/api-key.module.ts deleted file mode 100644 index daaa728a..00000000 --- a/apps/backend/src/api-key/api-key.module.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { ApiKeysService } from './api-key.service'; -import { ApiKeyController } from './api-key.controller'; -import { ApiKey } from './entities/api-key.entity'; -import { ApiRateLimitService } from './api-rate-limit.service'; -import { ApiKeyGuard } from './guards/api-key.guard'; -import { AuthModule } from '../modules/auth/auth.module'; - -@Module({ - imports: [TypeOrmModule.forFeature([ApiKey]), AuthModule], - controllers: [ApiKeyController], - providers: [ApiKeysService, ApiRateLimitService, ApiKeyGuard], - exports: [ApiKeysService, ApiRateLimitService, ApiKeyGuard], -}) -export class ApiKeyModule {} diff --git a/apps/backend/src/api-key/api-key.service.spec.ts b/apps/backend/src/api-key/api-key.service.spec.ts deleted file mode 100644 index 5f4bff56..00000000 --- a/apps/backend/src/api-key/api-key.service.spec.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ApiKeysService } from './api-key.service'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { ApiKey } from './entities/api-key.entity'; -import { Repository } from 'typeorm'; -import { NotFoundException } from '@nestjs/common'; - -describe('ApiKeysService', () => { - let service: ApiKeysService; - let repo: jest.Mocked>; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - ApiKeysService, - { - provide: getRepositoryToken(ApiKey), - useValue: { - create: jest.fn().mockImplementation((dto) => dto), - save: jest - .fn() - .mockImplementation((dto) => - Promise.resolve({ ...dto, id: 'k1', createdAt: new Date() }), - ), - findOne: jest.fn(), - find: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(ApiKeysService); - repo = module.get(getRepositoryToken(ApiKey)); - }); - - describe('create', () => { - it('should create and return raw key', async () => { - const result = await service.create('u1', { name: 'Test Key' }); - expect(result).toHaveProperty('key'); - expect(result.name).toBe('Test Key'); - expect(repo.save).toHaveBeenCalled(); - }); - }); - - describe('findByRawKey', () => { - it('should find by hash', async () => { - repo.findOne.mockResolvedValue({ id: 'k1' } as any); - const result = await service.findByRawKey('raw-key'); - expect(repo.findOne).toHaveBeenCalled(); - expect(result).toBeDefined(); - }); - }); - - describe('list', () => { - it('should return keys for user', async () => { - repo.find.mockResolvedValue([{ id: 'k1' }] as any); - const result = await service.list('u1'); - expect(result).toHaveLength(1); - }); - }); - - describe('revoke', () => { - it('should set active to false', async () => { - const mockKey = { id: 'k1', ownerUserId: 'u1', active: true }; - repo.findOne.mockResolvedValue(mockKey as any); - - await service.revoke('k1', 'u1'); - - expect(mockKey.active).toBe(false); - expect(repo.save).toHaveBeenCalled(); - }); - - it('should throw if not found', async () => { - repo.findOne.mockResolvedValue(null); - await expect(service.revoke('k1', 'u1')).rejects.toThrow( - NotFoundException, - ); - }); - }); -}); diff --git a/apps/backend/src/api-key/api-key.service.ts b/apps/backend/src/api-key/api-key.service.ts deleted file mode 100644 index 3671c3f8..00000000 --- a/apps/backend/src/api-key/api-key.service.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { ApiKey } from './entities/api-key.entity'; -import { generateApiKey, hashKey } from './utils/api-key.util'; - -interface CreateApiKeyDto { - name: string; - rateLimitPerMinute?: number; -} - -@Injectable() -export class ApiKeysService { - constructor( - @InjectRepository(ApiKey) - private repo: Repository, - ) {} - - async create(ownerUserId: string, dto: CreateApiKeyDto) { - const rawKey = generateApiKey(); - - const apiKey = this.repo.create({ - name: dto.name, - ownerUserId, - keyHash: hashKey(rawKey), - rateLimitPerMinute: - dto.rateLimitPerMinute ?? Number(process.env.DEFAULT_RATE_LIMIT ?? 60), - }); - - const saved = await this.repo.save(apiKey); - - return { - id: saved.id, - name: saved.name, - key: rawKey, - rateLimitPerMinute: saved.rateLimitPerMinute, - createdAt: saved.createdAt, - }; - } - - async findByRawKey(rawKey: string): Promise { - const keyHash = hashKey(rawKey); - return this.repo.findOne({ where: { keyHash } }); - } - - async list(ownerUserId: string) { - return this.repo.find({ - where: { ownerUserId }, - select: ['id', 'name', 'active', 'rateLimitPerMinute', 'createdAt'], - }); - } - - async revoke(id: string, ownerUserId: string) { - const key = await this.repo.findOne({ - where: { id, ownerUserId }, - }); - - if (!key) throw new NotFoundException(); - - key.active = false; - key.revokedAt = new Date(); - - return this.repo.save(key); - } -} diff --git a/apps/backend/src/api-key/api-rate-limit.service.ts b/apps/backend/src/api-key/api-rate-limit.service.ts deleted file mode 100644 index 5006cc84..00000000 --- a/apps/backend/src/api-key/api-rate-limit.service.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; - -interface RateLimitRecord { - count: number; - resetAt: number; -} - -export interface RateLimitInfo { - limit: number; - remaining: number; - resetAt: number; -} - -@Injectable() -export class ApiRateLimitService { - private readonly usage = new Map(); - private readonly windowMs = 60 * 1000; - - check(keyId: string, limit: number): RateLimitInfo { - const now = Date.now(); - const record = this.usage.get(keyId); - - if (!record || now > record.resetAt) { - const resetAt = now + this.windowMs; - this.usage.set(keyId, { count: 1, resetAt }); - return { limit, remaining: limit - 1, resetAt }; - } - - if (record.count >= limit) { - throw new HttpException( - { - statusCode: HttpStatus.TOO_MANY_REQUESTS, - message: 'Rate limit exceeded. Please retry after the reset window.', - retryAfter: Math.ceil((record.resetAt - now) / 1000), - }, - HttpStatus.TOO_MANY_REQUESTS, - ); - } - - record.count++; - return { limit, remaining: limit - record.count, resetAt: record.resetAt }; - } -} diff --git a/apps/backend/src/api-key/dto/create-api-key.dto.ts b/apps/backend/src/api-key/dto/create-api-key.dto.ts deleted file mode 100644 index 7058e750..00000000 --- a/apps/backend/src/api-key/dto/create-api-key.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsString, IsOptional, IsInt, Min } from 'class-validator'; - -export class CreateApiKeyDto { - @IsString() - name: string; - - @IsOptional() - @IsInt() - @Min(1) - rateLimitPerMinute?: number; -} diff --git a/apps/backend/src/api-key/dto/update-api-key.dto.ts b/apps/backend/src/api-key/dto/update-api-key.dto.ts deleted file mode 100644 index 8f355dce..00000000 --- a/apps/backend/src/api-key/dto/update-api-key.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateApiKeyDto } from './create-api-key.dto'; - -export class UpdateApiKeyDto extends PartialType(CreateApiKeyDto) {} diff --git a/apps/backend/src/api-key/entities/api-key.entity.ts b/apps/backend/src/api-key/entities/api-key.entity.ts deleted file mode 100644 index 543c3f62..00000000 --- a/apps/backend/src/api-key/entities/api-key.entity.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, -} from 'typeorm'; - -export enum ApiKeyTier { - NONE = 'none', - FREE = 'free', - PRO = 'pro', -} - -// Rate limits per tier (requests per minute) -export const TIER_LIMITS: Record = { - [ApiKeyTier.NONE]: 60, - [ApiKeyTier.FREE]: 120, - [ApiKeyTier.PRO]: 600, -}; - -@Entity() -export class ApiKey { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - name: string; - - @Column({ unique: true }) - keyHash: string; - - @Column() - ownerUserId: string; - - @Column({ default: true }) - active: boolean; - - @Column({ nullable: true }) - revokedAt?: Date; - - @Column({ type: 'text', default: ApiKeyTier.FREE }) - tier: ApiKeyTier; - - @Column({ type: 'int', default: 120 }) - rateLimitPerMinute: number; - - @CreateDateColumn() - createdAt: Date; -} diff --git a/apps/backend/src/api-key/entities/create-api-key.entity.ts b/apps/backend/src/api-key/entities/create-api-key.entity.ts deleted file mode 100644 index 0bcf1d1e..00000000 --- a/apps/backend/src/api-key/entities/create-api-key.entity.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class CreateApiKeyDto { - name: string; - rateLimitPerMinute: number; -} diff --git a/apps/backend/src/api-key/guards/api-key.guard.ts b/apps/backend/src/api-key/guards/api-key.guard.ts deleted file mode 100644 index fbe236d2..00000000 --- a/apps/backend/src/api-key/guards/api-key.guard.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - CanActivate, - ExecutionContext, - Injectable, - UnauthorizedException, - ForbiddenException, -} from '@nestjs/common'; -import { Request, Response } from 'express'; -import { ApiKeysService } from '../api-key.service'; -import { ApiRateLimitService } from '../api-rate-limit.service'; -import { ApiKey, TIER_LIMITS } from '../entities/api-key.entity'; - -interface RequestWithApiKey extends Request { - apiKey?: ApiKey; -} - -@Injectable() -export class ApiKeyGuard implements CanActivate { - constructor( - private apiKeyService: ApiKeysService, - private rateLimitService: ApiRateLimitService, - ) {} - - async canActivate(context: ExecutionContext): Promise { - const http = context.switchToHttp(); - const req = http.getRequest(); - const res = http.getResponse(); - - const rawKey = req.header('X-API-Key'); - if (!rawKey) { - throw new UnauthorizedException('Missing API key'); - } - - const key = await this.apiKeyService.findByRawKey(rawKey); - - if (!key) { - throw new UnauthorizedException('Invalid API key'); - } - - if (!key.active) { - throw new ForbiddenException('API key revoked'); - } - - const limit = TIER_LIMITS[key.tier] ?? key.rateLimitPerMinute; - const info = this.rateLimitService.check(key.id, limit); - - res.setHeader('X-RateLimit-Limit', info.limit); - res.setHeader('X-RateLimit-Remaining', info.remaining); - res.setHeader('X-RateLimit-Reset', Math.ceil(info.resetAt / 1000)); - - req.apiKey = key; - return true; - } -} diff --git a/apps/backend/src/api-key/utils/api-key.util.ts b/apps/backend/src/api-key/utils/api-key.util.ts deleted file mode 100644 index d14dfa7a..00000000 --- a/apps/backend/src/api-key/utils/api-key.util.ts +++ /dev/null @@ -1,9 +0,0 @@ -import * as crypto from 'crypto'; - -export function generateApiKey(): string { - return crypto.randomBytes(32).toString('hex'); -} - -export function hashKey(key: string): string { - return crypto.createHash('sha256').update(key).digest('hex'); -} diff --git a/apps/backend/src/app-version/app-version.controller.ts b/apps/backend/src/app-version/app-version.controller.ts deleted file mode 100644 index 9868a6d3..00000000 --- a/apps/backend/src/app-version/app-version.controller.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { AppVersionService, AppVersionInfo } from './app-version.service'; - -@Controller('api/app') -export class AppVersionController { - constructor(private readonly appVersionService: AppVersionService) {} - - @Get('version') - getVersion(): AppVersionInfo { - return this.appVersionService.getVersionInfo(); - } -} diff --git a/apps/backend/src/app-version/app-version.module.ts b/apps/backend/src/app-version/app-version.module.ts deleted file mode 100644 index 4f38a820..00000000 --- a/apps/backend/src/app-version/app-version.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import { AppVersionController } from './app-version.controller'; -import { AppVersionService } from './app-version.service'; - -@Module({ - imports: [ConfigModule], - controllers: [AppVersionController], - providers: [AppVersionService], -}) -export class AppVersionModule {} diff --git a/apps/backend/src/app-version/app-version.service.ts b/apps/backend/src/app-version/app-version.service.ts deleted file mode 100644 index b3dd5164..00000000 --- a/apps/backend/src/app-version/app-version.service.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; - -export interface AppVersionInfo { - minSupportedVersion: string; - latestVersion: string; - updateUrl: string; -} - -@Injectable() -export class AppVersionService { - constructor(private readonly configService: ConfigService) {} - - getVersionInfo(): AppVersionInfo { - return { - minSupportedVersion: this.configService.get( - 'APP_MIN_SUPPORTED_VERSION', - '1.0.0', - ), - latestVersion: this.configService.get( - 'APP_LATEST_VERSION', - '1.0.0', - ), - updateUrl: this.configService.get( - 'APP_UPDATE_URL', - 'https://apps.apple.com/app/vaultix/id0000000000', - ), - }; - } -} diff --git a/apps/backend/src/app.controller.spec.ts b/apps/backend/src/app.controller.spec.ts deleted file mode 100644 index d22f3890..00000000 --- a/apps/backend/src/app.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; - -describe('AppController', () => { - let appController: AppController; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - - appController = app.get(AppController); - }); - - describe('root', () => { - it('should return "Hello World!"', () => { - expect(appController.getHello()).toBe('Hello World!'); - }); - }); -}); diff --git a/apps/backend/src/app.controller.ts b/apps/backend/src/app.controller.ts deleted file mode 100644 index cce879ee..00000000 --- a/apps/backend/src/app.controller.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; -import { AppService } from './app.service'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello(): string { - return this.appService.getHello(); - } -} diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts deleted file mode 100644 index e3ec58a5..00000000 --- a/apps/backend/src/app.module.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { Module, forwardRef } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { ScheduleModule } from '@nestjs/schedule'; -import { JwtModule } from '@nestjs/jwt'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { AuthModule } from './modules/auth/auth.module'; -import { UserModule } from './modules/user/user.module'; -import { StellarModule } from './modules/stellar/stellar.module'; -import { WebhookModule } from './modules/webhook/webhook.module'; -import { User } from './modules/user/entities/user.entity'; -import { RefreshToken } from './modules/user/entities/refresh-token.entity'; -import { EmailVerification } from './modules/user/entities/email-verification.entity'; -import { Escrow } from './modules/escrow/entities/escrow.entity'; -import { Party } from './modules/escrow/entities/party.entity'; -import { Condition } from './modules/escrow/entities/condition.entity'; -import { EscrowEvent } from './modules/escrow/entities/escrow-event.entity'; -import { Dispute } from './modules/escrow/entities/dispute.entity'; -import { NotificationsModule } from './notifications/notifications.module'; -import { EscrowModule } from './modules/escrow/escrow.module'; -import { ApiKeyModule } from './api-key/api-key.module'; -import { Notification } from './notifications/entities/notification.entity'; -import { NotificationPreference } from './notifications/entities/notification-preference.entity'; -import { ApiKey } from './api-key/entities/api-key.entity'; -import { AdminAuditLog } from './modules/admin/entities/admin-audit-log.entity'; -import { Webhook } from './modules/webhook/webhook.entity'; -import { WebhookDelivery } from './modules/webhook/entities/webhook-delivery.entity'; -import { StellarEvent } from './modules/stellar/entities/stellar-event.entity'; -import { AdminModule } from './modules/admin/admin.module'; -import { StellarEventModule } from './modules/stellar/stellar-event.module'; -import { AssetsModule } from './modules/assets/assets.module'; -import { AllowedAsset } from './modules/assets/entities/allowed-asset.entity'; -import { IpfsModule } from './modules/ipfs/ipfs.module'; -import { HealthModule } from './modules/health/health.module'; -import { AppVersionModule } from './app-version/app-version.module'; -import stellarConfig from './config/stellar.config'; -import ipfsConfig from './config/ipfs.config'; - -@Module({ - imports: [ - ConfigModule.forRoot({ - isGlobal: true, - load: [stellarConfig, ipfsConfig], - }), - ScheduleModule.forRoot(), - TypeOrmModule.forRootAsync({ - imports: [ConfigModule], - useFactory: (configService: ConfigService) => ({ - type: 'sqlite', - database: configService.get( - 'DATABASE_PATH', - './data/vaultix.db', - ), - entities: [ - User, - RefreshToken, - EmailVerification, - Escrow, - Party, - Condition, - EscrowEvent, - Dispute, - Notification, - NotificationPreference, - ApiKey, - AdminAuditLog, - Webhook, - WebhookDelivery, - StellarEvent, - AllowedAsset, - ], - synchronize: process.env.NODE_ENV === 'test', - migrations: [__dirname + '/migrations/*.ts'], - migrationsRun: process.env.NODE_ENV !== 'test', - }), - inject: [ConfigService], - }), - AuthModule, - UserModule, - EscrowModule, - StellarModule, - forwardRef(() => AdminModule), - WebhookModule, - NotificationsModule, - ApiKeyModule, - forwardRef(() => StellarEventModule), - AssetsModule, - IpfsModule, - HealthModule, - AppVersionModule, - JwtModule.registerAsync({ - useFactory: (configService: ConfigService) => ({ - secret: - configService.get('JWT_SECRET') || - 'your-secret-key-change-in-production', - signOptions: { expiresIn: '15m' }, - }), - inject: [ConfigService], - }), - ], - controllers: [AppController], - providers: [AppService], -}) -export class AppModule {} diff --git a/apps/backend/src/app.service.ts b/apps/backend/src/app.service.ts deleted file mode 100644 index 927d7cca..00000000 --- a/apps/backend/src/app.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class AppService { - getHello(): string { - return 'Hello World!'; - } -} diff --git a/apps/backend/src/clients/auth.ts b/apps/backend/src/clients/auth.ts deleted file mode 100644 index 3a092c0f..00000000 --- a/apps/backend/src/clients/auth.ts +++ /dev/null @@ -1,38 +0,0 @@ -import api from './client'; -import { setTokens } from '../utils/token'; - -interface AuthResponse { - accessToken: string; - refreshToken: string; - user?: Record; -} - -interface RegisterData { - email: string; - password: string; - name?: string; -} - -export const login = async ( - email: string, - password: string, -): Promise => { - const response = await api.post('/auth/login', { - email, - password, - }); - - const { accessToken, refreshToken } = response.data; - setTokens(accessToken, refreshToken); - - return response.data; -}; - -export const register = async (data: RegisterData): Promise => { - const response = await api.post('/auth/register', data); - return response.data; -}; - -export const logout = async (): Promise => { - await api.post('/auth/logout'); -}; diff --git a/apps/backend/src/clients/client.ts b/apps/backend/src/clients/client.ts deleted file mode 100644 index 2e090398..00000000 --- a/apps/backend/src/clients/client.ts +++ /dev/null @@ -1,99 +0,0 @@ -import axios, { - AxiosError, - InternalAxiosRequestConfig, - AxiosResponse, -} from 'axios'; -import { getAccessToken, getRefreshToken, setTokens } from '../utils/token'; -import { clearTokens } from '../utils/token'; - -const API_URL = process.env.VITE_API_URL || 'http://localhost:3001'; - -const api = axios.create({ - baseURL: API_URL, - withCredentials: true, -}); - -interface AuthTokens { - accessToken: string; - refreshToken: string; -} - -interface WindowWithLocation { - location: { - href: string; - }; -} - -// 🔄 RESPONSE INTERCEPTOR (Refresh Flow) -let isRefreshing = false; - -api.interceptors.request.use( - (config) => { - const token = getAccessToken(); - - if (token) { - config.headers.Authorization = `Bearer ${token}`; - } - - return config; - }, - (error: AxiosError): Promise => { - return Promise.reject(new Error(error.message)); - }, -); - -api.interceptors.response.use( - (response: AxiosResponse) => response, - async (error: AxiosError): Promise => { - const originalRequest = error.config as InternalAxiosRequestConfig & { - _retry?: boolean; - }; - - if ( - error.response?.status === 401 && - originalRequest && - !originalRequest._retry - ) { - if (isRefreshing) { - // If already refreshing, just reject - caller will retry - return Promise.reject(error); - } - - originalRequest._retry = true; - isRefreshing = true; - - try { - const refreshToken = getRefreshToken(); - - const response = await axios.post( - `${API_URL}/auth/refresh`, - { refreshToken }, - ); - - const { accessToken, refreshToken: newRefreshToken } = response.data; - - setTokens(accessToken, newRefreshToken); - - if (originalRequest.headers) { - originalRequest.headers['Authorization'] = `Bearer ${accessToken}`; - } - - return api(originalRequest); - } catch (err) { - const error = err instanceof Error ? err : new Error(String(err)); - clearTokens(); - if (typeof globalThis.window !== 'undefined') { - const win = globalThis.window as WindowWithLocation; - win.location.href = '/login'; - } - return Promise.reject(error); - } finally { - isRefreshing = false; - } - } - - return Promise.reject(new Error(error.message)); - }, -); - -export default api; diff --git a/apps/backend/src/config/ipfs.config.ts b/apps/backend/src/config/ipfs.config.ts deleted file mode 100644 index b396f106..00000000 --- a/apps/backend/src/config/ipfs.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { registerAs } from '@nestjs/config'; - -export default registerAs('ipfs', () => ({ - provider: process.env.IPFS_PROVIDER || 'pinata', - pinataApiKey: process.env.PINATA_API_KEY, - pinataSecretApiKey: process.env.PINATA_SECRET_API_KEY, - pinataJwt: process.env.PINATA_JWT, - gatewayUrl: - process.env.IPFS_GATEWAY_URL || 'https://gateway.pinata.cloud/ipfs/', - localNodeUrl: process.env.IPFS_LOCAL_NODE_URL || 'http://localhost:5001', - maxRetries: parseInt(process.env.IPFS_MAX_RETRIES || '1', 10), -})); diff --git a/apps/backend/src/config/stellar.config.ts b/apps/backend/src/config/stellar.config.ts index a9e95c15..926e53dd 100644 --- a/apps/backend/src/config/stellar.config.ts +++ b/apps/backend/src/config/stellar.config.ts @@ -1,7 +1,7 @@ import { registerAs } from '@nestjs/config'; export default registerAs('stellar', () => ({ - network: process.env.STELLAR_NETWORK || 'testnet', // 'testnet' or 'mainnet' + network: process.env.STELLAR_NETWORK || 'testnet', horizonUrl: process.env.HORIZON_URL || (process.env.STELLAR_NETWORK === 'mainnet' @@ -13,7 +13,11 @@ export default registerAs('stellar', () => ({ ? 'Public Global Stellar Network ; September 2015' : 'Test SDF Network ; September 2015'), walletSecret: process.env.WALLET_SECRET || '', - timeout: parseInt(process.env.STELLAR_TIMEOUT || '60000', 10), // 60 seconds + timeout: parseInt(process.env.STELLAR_TIMEOUT || '60000', 10), maxRetries: parseInt(process.env.STELLAR_MAX_RETRIES || '3', 10), - retryDelay: parseInt(process.env.STELLAR_RETRY_DELAY || '1000', 10), // 1 second base delay -})); + retryDelay: parseInt(process.env.STELLAR_RETRY_DELAY || '1000', 10), + circuitBreakerMaxFailures: parseInt(process.env.STELLAR_CIRCUIT_BREAKER_MAX_FAILURES || '5', 10), + circuitBreakerResetTimeout: parseInt(process.env.STELLAR_CIRCUIT_BREAKER_RESET_TIMEOUT || '60000', 10), + retryJitter: parseFloat(process.env.STELLAR_RETRY_JITTER || '0.1'), + retryFactor: parseFloat(process.env.STELLAR_RETRY_FACTOR || '2.0'), +})); \ No newline at end of file diff --git a/apps/backend/src/data-source.ts b/apps/backend/src/data-source.ts deleted file mode 100644 index 518bc5d9..00000000 --- a/apps/backend/src/data-source.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { DataSource } from 'typeorm'; -import { config } from 'dotenv'; -import { User } from './modules/user/entities/user.entity'; -import { RefreshToken } from './modules/user/entities/refresh-token.entity'; -import { Escrow } from './modules/escrow/entities/escrow.entity'; -import { Party } from './modules/escrow/entities/party.entity'; -import { Condition } from './modules/escrow/entities/condition.entity'; -import { EscrowEvent } from './modules/escrow/entities/escrow-event.entity'; -import { Dispute } from './modules/escrow/entities/dispute.entity'; -import { Notification } from './notifications/entities/notification.entity'; -import { NotificationPreference } from './notifications/entities/notification-preference.entity'; -import { ApiKey } from './api-key/entities/api-key.entity'; -import { AdminAuditLog } from './modules/admin/entities/admin-audit-log.entity'; -import { Webhook } from './modules/webhook/webhook.entity'; -import { WebhookDelivery } from './modules/webhook/entities/webhook-delivery.entity'; -import { StellarEvent } from './modules/stellar/entities/stellar-event.entity'; -import { AllowedAsset } from './modules/assets/entities/allowed-asset.entity'; - -config(); // Load .env file - -export default new DataSource({ - type: 'sqlite', - database: process.env.DATABASE_PATH || './data/vaultix.db', - entities: [ - User, - RefreshToken, - Escrow, - Party, - Condition, - EscrowEvent, - Dispute, - Notification, - NotificationPreference, - ApiKey, - AdminAuditLog, - Webhook, - WebhookDelivery, - StellarEvent, - AllowedAsset, - ], - migrations: ['./src/migrations/*.ts'], - synchronize: false, -}); diff --git a/apps/backend/src/entities/condition.entity.ts b/apps/backend/src/entities/condition.entity.ts deleted file mode 100644 index a08e16a1..00000000 --- a/apps/backend/src/entities/condition.entity.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm'; - -@Entity('conditions') -export class Condition { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - @Index() // Speeds up lookups when joining or querying conditions by escrow - escrowId: string; - - @Column() - title: string; -} diff --git a/apps/backend/src/entities/dispute.entity.ts b/apps/backend/src/entities/dispute.entity.ts deleted file mode 100644 index 991940ea..00000000 --- a/apps/backend/src/entities/dispute.entity.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm'; - -@Entity('disputes') -export class Dispute { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - @Index() - escrowId: string; - - @Column({ type: 'varchar', length: 32 }) - @Index() - status: string; -} diff --git a/apps/backend/src/entities/escrow.entity.ts b/apps/backend/src/entities/escrow.entity.ts deleted file mode 100644 index 46c919a7..00000000 --- a/apps/backend/src/entities/escrow.entity.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - Index, -} from 'typeorm'; - -@Entity('escrows') -// Composite indices matching execution WHERE and ORDER BY logic -@Index(['status', 'createdAt']) -@Index(['buyerId', 'status']) -@Index(['sellerId', 'status']) -export class Escrow { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - @Index() // Single-column query filter acceleration - buyerId: string; - - @Column() - @Index() - sellerId: string; - - @Column({ type: 'varchar', length: 32 }) - @Index() - status: string; - - @Column({ type: 'timestamp' }) - @Index() - deadline: Date; - - @CreateDateColumn() - @Index() - createdAt: Date; -} diff --git a/apps/backend/src/entities/notification.entity.ts b/apps/backend/src/entities/notification.entity.ts deleted file mode 100644 index 6f36354d..00000000 --- a/apps/backend/src/entities/notification.entity.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm'; - -@Entity('notifications') -// Composite optimization index for fetching unread notification feeds per user -@Index(['userId', 'isRead']) -export class Notification { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - userId: string; - - @Column({ type: 'boolean', default: false }) - isRead: boolean; - - @Column() - message: string; -} diff --git a/apps/backend/src/exceptions/stellar-rpc-exception.ts b/apps/backend/src/exceptions/stellar-rpc-exception.ts new file mode 100644 index 00000000..30fcb6f8 --- /dev/null +++ b/apps/backend/src/exceptions/stellar-rpc-exception.ts @@ -0,0 +1,58 @@ +import { HttpException, HttpStatus } from '@nestjs/common'; + +export abstract class StellarRpcException extends HttpException { + public readonly code: string; + public readonly category: + | 'timeout' + | 'transaction' + | 'account' + | 'network' + | 'validation'; + + constructor( + message: string, + code: string, + status: HttpStatus, + category: + | 'timeout' + | 'transaction' + | 'account' + | 'network' + | 'validation', + public readonly isRetryable: boolean = false, + ) { + super(message, status); + this.code = code; + this.category = category; + } +} + +export class StellarRpcTimeoutException extends StellarRpcException { + constructor(message: string) { + super(message, 'RPC_TIMEOUT', HttpStatus.GATEWAY_TIMEOUT, 'timeout', true); + } +} + +export class StellarRpcTransactionException extends StellarRpcException { + constructor(message: string) { + super(message, 'RPC_TRANSACTION_ERROR', HttpStatus.BAD_GATEWAY, 'transaction'); + } +} + +export class StellarRpcAccountException extends StellarRpcException { + constructor(message: string) { + super(message, 'RPC_ACCOUNT_ERROR', HttpStatus.BAD_GATEWAY, 'account'); + } +} + +export class StellarRpcNetworkException extends StellarRpcException { + constructor(message: string) { + super(message, 'RPC_NETWORK_ERROR', HttpStatus.BAD_GATEWAY, 'network', true); + } +} + +export class StellarRpcValidationException extends StellarRpcException { + constructor(message: string) { + super(message, 'RPC_VALIDATION_ERROR', HttpStatus.BAD_REQUEST, 'validation', false); + } +} diff --git a/apps/backend/src/gateways/escrow.gateway.ts b/apps/backend/src/gateways/escrow.gateway.ts deleted file mode 100644 index 86581331..00000000 --- a/apps/backend/src/gateways/escrow.gateway.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { - WebSocketGateway, - WebSocketServer, - SubscribeMessage, - OnGatewayConnection, - OnGatewayDisconnect, -} from '@nestjs/websockets'; -import { Server, Socket } from 'socket.io'; -import { Logger } from '@nestjs/common'; -import { JwtService } from '@nestjs/jwt'; - -interface EscrowEventData { - [key: string]: unknown; -} - -@WebSocketGateway({ - cors: { - origin: process.env.FRONTEND_URL || 'http://localhost:3001', - credentials: true, - }, - namespace: 'escrow', -}) -export class EscrowGateway implements OnGatewayConnection, OnGatewayDisconnect { - @WebSocketServer() - server: Server; - - private readonly logger = new Logger(EscrowGateway.name); - private userSocketMap: Map = new Map(); // userId -> socketIds[] - private socketUserMap: Map = new Map(); // socketId -> userId - private socketEscrowMap: Map = new Map(); // socketId -> escrowIds[] - - constructor(private jwtService: JwtService) {} - - handleConnection(client: Socket) { - try { - // Extract token from handshake - const token = - (client.handshake.auth.token as string) || - (client.handshake.headers.authorization as string)?.split(' ')[1]; - - if (!token) { - this.logger.warn( - `Connection rejected: No token provided (${client.id})`, - ); - client.disconnect(); - return; - } - - // Verify JWT - const decoded: { sub?: string; userId?: string } = - this.jwtService.verify(token); - const userId: string | undefined = decoded?.sub || decoded?.userId; - - if (!userId) { - this.logger.warn(`Connection rejected: Invalid token (${client.id})`); - client.disconnect(); - return; - } - - // Store connection mapping - this.socketUserMap.set(client.id, userId); - const userSockets: string[] = this.userSocketMap.get(userId) || []; - userSockets.push(client.id); - this.userSocketMap.set(userId, userSockets); - - this.logger.log(`Client connected: ${client.id} (user: ${userId})`); - - // Send connection success - client.emit('connected', { userId, socketId: client.id }); - } catch (error: unknown) { - this.logger.error( - `Connection rejected: Invalid token (${client.id})`, - error, - ); - client.disconnect(); - } - } - - handleDisconnect(client: Socket): void { - const userId = this.socketUserMap.get(client.id); - if (userId) { - // Remove from user mapping - const userSockets = this.userSocketMap.get(userId) || []; - const updatedSockets = userSockets.filter((id) => id !== client.id); - if (updatedSockets.length === 0) { - this.userSocketMap.delete(userId); - } else { - this.userSocketMap.set(userId, updatedSockets); - } - this.socketUserMap.delete(client.id); - - // Clean up escroom subscriptions - const escrowIds = this.socketEscrowMap.get(client.id) || []; - escrowIds.forEach((escrowId) => { - void client.leave(`escrow:${escrowId}`); - }); - this.socketEscrowMap.delete(client.id); - - this.logger.log(`Client disconnected: ${client.id} (user: ${userId})`); - } - } - - @SubscribeMessage('joinEscrow') - handleJoinEscrow(client: Socket, escrowId: string): void { - const room = `escrow:${escrowId}`; - void client.join(room); - - // Track subscription - const escrowIds: string[] = this.socketEscrowMap.get(client.id) || []; - if (!escrowIds.includes(escrowId)) { - escrowIds.push(escrowId); - this.socketEscrowMap.set(client.id, escrowIds); - } - - this.logger.log(`Client ${client.id} joined escrow room: ${escrowId}`); - client.emit('joinedEscrow', { escrowId }); - } - - @SubscribeMessage('leaveEscrow') - handleLeaveEscrow(client: Socket, escrowId: string): void { - const room = `escrow:${escrowId}`; - void client.leave(room); - - // Remove from tracking - const escrowIds: string[] = this.socketEscrowMap.get(client.id) || []; - const updatedEscrowIds = escrowIds.filter((id) => id !== escrowId); - this.socketEscrowMap.set(client.id, updatedEscrowIds); - - this.logger.log(`Client ${client.id} left escrow room: ${escrowId}`); - } - - // Broadcast methods - called from EscrowService - broadcastEscrowStatusChanged(escrowId: string, data: EscrowEventData): void { - this.server.to(`escrow:${escrowId}`).emit('escrow:status_changed', { - escrowId, - ...data, - timestamp: new Date().toISOString(), - }); - } - - broadcastMilestoneReleased(escrowId: string, data: EscrowEventData): void { - this.server.to(`escrow:${escrowId}`).emit('escrow:milestone_released', { - escrowId, - ...data, - timestamp: new Date().toISOString(), - }); - } - - broadcastDisputeFiled(escrowId: string, data: EscrowEventData): void { - this.server.to(`escrow:${escrowId}`).emit('escrow:dispute_filed', { - escrowId, - ...data, - timestamp: new Date().toISOString(), - }); - } - - broadcastDisputeResolved(escrowId: string, data: EscrowEventData): void { - this.server.to(`escrow:${escrowId}`).emit('escrow:dispute_resolved', { - escrowId, - ...data, - timestamp: new Date().toISOString(), - }); - } - - broadcastPartyJoined(escrowId: string, data: EscrowEventData): void { - this.server.to(`escrow:${escrowId}`).emit('escrow:party_joined', { - escrowId, - ...data, - timestamp: new Date().toISOString(), - }); - } - - broadcastConditionFulfilled(escrowId: string, data: EscrowEventData): void { - this.server.to(`escrow:${escrowId}`).emit('escrow:condition_fulfilled', { - escrowId, - ...data, - timestamp: new Date().toISOString(), - }); - } - - broadcastConditionConfirmed(escrowId: string, data: EscrowEventData): void { - this.server.to(`escrow:${escrowId}`).emit('escrow:condition_confirmed', { - escrowId, - ...data, - timestamp: new Date().toISOString(), - }); - } - - broadcastNotification(userId: string, data: EscrowEventData): void { - const socketIds = this.userSocketMap.get(userId) || []; - socketIds.forEach((socketId) => { - this.server.to(socketId).emit('notification:new', { - ...data, - timestamp: new Date().toISOString(), - }); - }); - } - - broadcastEscrowFunded(escrowId: string, data: EscrowEventData): void { - this.server.to(`escrow:${escrowId}`).emit('escrow:funded', { - escrowId, - ...data, - timestamp: new Date().toISOString(), - }); - } - - broadcastEscrowCompleted(escrowId: string, data: EscrowEventData): void { - this.server.to(`escrow:${escrowId}`).emit('escrow:completed', { - escrowId, - ...data, - timestamp: new Date().toISOString(), - }); - } - - broadcastEscrowCancelled(escrowId: string, data: EscrowEventData): void { - this.server.to(`escrow:${escrowId}`).emit('escrow:cancelled', { - escrowId, - ...data, - timestamp: new Date().toISOString(), - }); - } - - // Get online users (for admin/monitoring) - getOnlineUsers(): Map { - return this.userSocketMap; - } - - // Get user's socket IDs - getUserSockets(userId: string): string[] { - return this.userSocketMap.get(userId) || []; - } - - // Check if user is online - isUserOnline(userId: string): boolean { - const sockets = this.userSocketMap.get(userId) || []; - return sockets.length > 0; - } - isHealthy(): boolean { - return this.server !== undefined; - } -} diff --git a/apps/backend/src/gateways/gateways.module.ts b/apps/backend/src/gateways/gateways.module.ts deleted file mode 100644 index 77eecbbe..00000000 --- a/apps/backend/src/gateways/gateways.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { JwtModule } from '@nestjs/jwt'; -import { EscrowGateway } from './escrow.gateway'; - -@Module({ - imports: [JwtModule], - providers: [EscrowGateway], - exports: [EscrowGateway], -}) -export class GatewaysModule {} diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts deleted file mode 100644 index a88e16fb..00000000 --- a/apps/backend/src/main.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { NestFactory } from '@nestjs/core'; -import { ValidationPipe, VersioningType } from '@nestjs/common'; -import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { AppModule } from './app.module'; - -async function bootstrap() { - const app = await NestFactory.create(AppModule); - - // URI versioning — all routes become /v1/... - app.enableVersioning({ - type: VersioningType.URI, - defaultVersion: '1', - }); - - // Enable global validation - app.useGlobalPipes( - new ValidationPipe({ - whitelist: true, - forbidNonWhitelisted: true, - transform: true, - }), - ); - - const swaggerConfig = new DocumentBuilder() - .setTitle('Vaultix Backend API') - .setDescription('Vaultix backend endpoints') - .setVersion('1.0') - .addBearerAuth() - .build(); - - const swaggerDocument = SwaggerModule.createDocument(app, swaggerConfig); - SwaggerModule.setup('api/docs', app, swaggerDocument); - - await app.listen(process.env.PORT ?? 3000); -} -void bootstrap(); diff --git a/apps/backend/src/migrations/1774364374006-InitialMigration.ts b/apps/backend/src/migrations/1774364374006-InitialMigration.ts deleted file mode 100644 index 778e6c36..00000000 --- a/apps/backend/src/migrations/1774364374006-InitialMigration.ts +++ /dev/null @@ -1,215 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class InitialMigration1774364374006 implements MigrationInterface { - name = 'InitialMigration1774364374006'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "temporary_users" ("id" varchar PRIMARY KEY NOT NULL, "walletAddress" varchar NOT NULL, "nonce" varchar, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "role" varchar CHECK( "role" IN ('USER','ADMIN','SUPER_ADMIN') ) NOT NULL DEFAULT ('USER'), CONSTRAINT "UQ_fc71cd6fb73f95244b23e2ef113" UNIQUE ("walletAddress"))`, - ); - await queryRunner.query( - `INSERT INTO "temporary_users"("id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role") SELECT "id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role" FROM "users"`, - ); - await queryRunner.query(`DROP TABLE "users"`); - await queryRunner.query(`ALTER TABLE "temporary_users" RENAME TO "users"`); - await queryRunner.query( - `CREATE TABLE "disputes" ("id" varchar PRIMARY KEY NOT NULL, "escrowId" varchar NOT NULL, "filedByUserId" varchar NOT NULL, "reason" text NOT NULL, "evidence" text, "status" varchar NOT NULL DEFAULT ('open'), "resolvedByUserId" varchar, "resolutionNotes" text, "sellerPercent" decimal(5,2), "buyerPercent" decimal(5,2), "outcome" varchar, "resolvedAt" datetime, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "REL_05cc0a36ac22b4dc8a82211d2e" UNIQUE ("escrowId"))`, - ); - await queryRunner.query( - `CREATE TABLE "notification" ("id" varchar PRIMARY KEY NOT NULL, "userId" varchar NOT NULL, "eventType" varchar CHECK( "eventType" IN ('ESCROW_CREATED','ESCROW_FUNDED','MILESTONE_RELEASED','ESCROW_COMPLETED','ESCROW_CANCELLED','DISPUTE_RAISED','DISPUTE_RESOLVED','ESCROW_EXPIRED') ) NOT NULL, "payload" text NOT NULL, "status" varchar CHECK( "status" IN ('pending','sent','failed') ) NOT NULL DEFAULT ('pending'), "retryCount" integer NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`, - ); - await queryRunner.query( - `CREATE TABLE "notification_preference" ("id" varchar PRIMARY KEY NOT NULL, "userId" varchar NOT NULL, "channel" varchar CHECK( "channel" IN ('email','webhook') ) NOT NULL, "enabled" boolean NOT NULL DEFAULT (1), "eventTypes" text NOT NULL, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`, - ); - await queryRunner.query( - `CREATE TABLE "api_key" ("id" varchar PRIMARY KEY NOT NULL, "name" varchar NOT NULL, "keyHash" varchar NOT NULL, "ownerUserId" varchar NOT NULL, "active" boolean NOT NULL DEFAULT (1), "revokedAt" datetime, "rateLimitPerMinute" integer NOT NULL DEFAULT (60), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "UQ_4aacb7c1641a74534c8a96c4dc9" UNIQUE ("keyHash"))`, - ); - await queryRunner.query( - `CREATE TABLE "admin_audit_log" ("id" varchar PRIMARY KEY NOT NULL, "actorId" varchar(64) NOT NULL, "actionType" varchar(64) NOT NULL, "resourceType" varchar(64) NOT NULL, "resourceId" varchar(128), "metadata" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')))`, - ); - await queryRunner.query( - `CREATE TABLE "webhooks" ("id" varchar PRIMARY KEY NOT NULL, "url" varchar NOT NULL, "secret" varchar NOT NULL, "events" text NOT NULL, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "userId" varchar NOT NULL)`, - ); - await queryRunner.query( - `CREATE TABLE "stellar_events" ("id" varchar PRIMARY KEY NOT NULL, "txHash" varchar(64) NOT NULL, "eventIndex" integer NOT NULL, "eventType" varchar CHECK( "eventType" IN ('ESCROW_CREATED','ESCROW_FUNDED','MILESTONE_RELEASED','ESCROW_COMPLETED','ESCROW_CANCELLED','DISPUTE_CREATED','DISPUTE_RESOLVED') ) NOT NULL, "escrowId" varchar, "ledger" integer NOT NULL, "timestamp" datetime NOT NULL, "rawPayload" text NOT NULL, "extractedFields" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "amount" decimal(18,7), "asset" varchar, "milestoneIndex" integer, "fromAddress" varchar, "toAddress" varchar, "reason" text, CONSTRAINT "UQ_c6a70c31de4c4d3b23e6229c515" UNIQUE ("txHash", "eventIndex"))`, - ); - await queryRunner.query( - `CREATE INDEX "IDX_33bcf3b402d8757d22c1cc1ad3" ON "stellar_events" ("txHash") `, - ); - await queryRunner.query( - `CREATE INDEX "IDX_56e937f62e6766e06118ae9b6c" ON "stellar_events" ("eventType") `, - ); - await queryRunner.query( - `CREATE INDEX "IDX_ad3aa47d032fd93d4f307d30ab" ON "stellar_events" ("ledger") `, - ); - await queryRunner.query( - `CREATE INDEX "IDX_f8d00fffa3b5110edf867481dd" ON "stellar_events" ("timestamp") `, - ); - await queryRunner.query( - `CREATE TABLE "temporary_escrow_conditions" ("id" varchar PRIMARY KEY NOT NULL, "escrowId" varchar NOT NULL, "description" text NOT NULL, "type" varchar NOT NULL DEFAULT ('manual'), "isMet" boolean NOT NULL DEFAULT (0), "metAt" datetime, "metByUserId" varchar, "metadata" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "isFulfilled" boolean NOT NULL DEFAULT (0), "fulfilledAt" datetime, "fulfilledByUserId" varchar, "fulfillmentNotes" text, "fulfillmentEvidence" text, CONSTRAINT "FK_88456ecac834c788fc912233e05" FOREIGN KEY ("escrowId") REFERENCES "escrows" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`, - ); - await queryRunner.query( - `INSERT INTO "temporary_escrow_conditions"("id", "escrowId", "description", "type", "isMet", "metAt", "metByUserId", "metadata", "createdAt", "updatedAt") SELECT "id", "escrowId", "description", "type", "isMet", "metAt", "metByUserId", "metadata", "createdAt", "updatedAt" FROM "escrow_conditions"`, - ); - await queryRunner.query(`DROP TABLE "escrow_conditions"`); - await queryRunner.query( - `ALTER TABLE "temporary_escrow_conditions" RENAME TO "escrow_conditions"`, - ); - await queryRunner.query(`DROP INDEX "idx_escrows_creator"`); - await queryRunner.query(`DROP INDEX "idx_escrows_status"`); - await queryRunner.query(`DROP INDEX "idx_escrows_asset"`); - await queryRunner.query(`DROP INDEX "idx_escrows_created_at"`); - await queryRunner.query(`DROP INDEX "idx_escrows_expires_at"`); - await queryRunner.query(`DROP INDEX "idx_escrows_creator_status_created"`); - await queryRunner.query( - `CREATE TABLE "temporary_escrows" ("id" varchar PRIMARY KEY NOT NULL, "title" varchar NOT NULL, "description" text, "amount" decimal(18,7) NOT NULL, "asset" varchar NOT NULL DEFAULT ('XLM'), "status" varchar NOT NULL DEFAULT ('pending'), "type" varchar NOT NULL DEFAULT ('standard'), "creatorId" varchar NOT NULL, "expiresAt" datetime, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "releaseTransactionHash" varchar, "isReleased" boolean NOT NULL DEFAULT (0), "expirationNotifiedAt" datetime, "stellarTxHash" varchar, "fundedAt" datetime, CONSTRAINT "FK_b483b9f73c28476240bbc993d0b" FOREIGN KEY ("creatorId") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`, - ); - await queryRunner.query( - `INSERT INTO "temporary_escrows"("id", "title", "description", "amount", "asset", "status", "type", "creatorId", "expiresAt", "isActive", "createdAt", "updatedAt", "releaseTransactionHash", "isReleased", "expirationNotifiedAt") SELECT "id", "title", "description", "amount", "asset", "status", "type", "creatorId", "expiresAt", "isActive", "createdAt", "updatedAt", "releaseTransactionHash", "isReleased", "expirationNotifiedAt" FROM "escrows"`, - ); - await queryRunner.query(`DROP TABLE "escrows"`); - await queryRunner.query( - `ALTER TABLE "temporary_escrows" RENAME TO "escrows"`, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_creator" ON "escrows" ("creatorId") `, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_status" ON "escrows" ("status") `, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_asset" ON "escrows" ("asset") `, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_created_at" ON "escrows" ("createdAt") `, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_expires_at" ON "escrows" ("expiresAt") `, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_creator_status_created" ON "escrows" ("creatorId", "status", "createdAt") `, - ); - await queryRunner.query( - `CREATE TABLE "temporary_users" ("id" varchar PRIMARY KEY NOT NULL, "walletAddress" varchar NOT NULL, "nonce" varchar, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "role" varchar CHECK( "role" IN ('USER','ADMIN','SUPER_ADMIN') ) NOT NULL DEFAULT ('USER'), CONSTRAINT "UQ_fc71cd6fb73f95244b23e2ef113" UNIQUE ("walletAddress"))`, - ); - await queryRunner.query( - `INSERT INTO "temporary_users"("id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role") SELECT "id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role" FROM "users"`, - ); - await queryRunner.query(`DROP TABLE "users"`); - await queryRunner.query(`ALTER TABLE "temporary_users" RENAME TO "users"`); - await queryRunner.query( - `CREATE TABLE "temporary_disputes" ("id" varchar PRIMARY KEY NOT NULL, "escrowId" varchar NOT NULL, "filedByUserId" varchar NOT NULL, "reason" text NOT NULL, "evidence" text, "status" varchar NOT NULL DEFAULT ('open'), "resolvedByUserId" varchar, "resolutionNotes" text, "sellerPercent" decimal(5,2), "buyerPercent" decimal(5,2), "outcome" varchar, "resolvedAt" datetime, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "REL_05cc0a36ac22b4dc8a82211d2e" UNIQUE ("escrowId"), CONSTRAINT "FK_05cc0a36ac22b4dc8a82211d2ee" FOREIGN KEY ("escrowId") REFERENCES "escrows" ("id") ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT "FK_23f126d4aaaa9d67243895b784b" FOREIGN KEY ("filedByUserId") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT "FK_a2215614b176b851d3e431de9b9" FOREIGN KEY ("resolvedByUserId") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`, - ); - await queryRunner.query( - `INSERT INTO "temporary_disputes"("id", "escrowId", "filedByUserId", "reason", "evidence", "status", "resolvedByUserId", "resolutionNotes", "sellerPercent", "buyerPercent", "outcome", "resolvedAt", "createdAt", "updatedAt") SELECT "id", "escrowId", "filedByUserId", "reason", "evidence", "status", "resolvedByUserId", "resolutionNotes", "sellerPercent", "buyerPercent", "outcome", "resolvedAt", "createdAt", "updatedAt" FROM "disputes"`, - ); - await queryRunner.query(`DROP TABLE "disputes"`); - await queryRunner.query( - `ALTER TABLE "temporary_disputes" RENAME TO "disputes"`, - ); - await queryRunner.query( - `CREATE TABLE "temporary_webhooks" ("id" varchar PRIMARY KEY NOT NULL, "url" varchar NOT NULL, "secret" varchar NOT NULL, "events" text NOT NULL, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "userId" varchar NOT NULL, CONSTRAINT "FK_7dbbb3fa9d7ccab4925a67af414" FOREIGN KEY ("userId") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`, - ); - await queryRunner.query( - `INSERT INTO "temporary_webhooks"("id", "url", "secret", "events", "isActive", "createdAt", "updatedAt", "userId") SELECT "id", "url", "secret", "events", "isActive", "createdAt", "updatedAt", "userId" FROM "webhooks"`, - ); - await queryRunner.query(`DROP TABLE "webhooks"`); - await queryRunner.query( - `ALTER TABLE "temporary_webhooks" RENAME TO "webhooks"`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "webhooks" RENAME TO "temporary_webhooks"`, - ); - await queryRunner.query( - `CREATE TABLE "webhooks" ("id" varchar PRIMARY KEY NOT NULL, "url" varchar NOT NULL, "secret" varchar NOT NULL, "events" text NOT NULL, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "userId" varchar NOT NULL)`, - ); - await queryRunner.query( - `INSERT INTO "webhooks"("id", "url", "secret", "events", "isActive", "createdAt", "updatedAt", "userId") SELECT "id", "url", "secret", "events", "isActive", "createdAt", "updatedAt", "userId" FROM "temporary_webhooks"`, - ); - await queryRunner.query(`DROP TABLE "temporary_webhooks"`); - await queryRunner.query( - `ALTER TABLE "disputes" RENAME TO "temporary_disputes"`, - ); - await queryRunner.query( - `CREATE TABLE "disputes" ("id" varchar PRIMARY KEY NOT NULL, "escrowId" varchar NOT NULL, "filedByUserId" varchar NOT NULL, "reason" text NOT NULL, "evidence" text, "status" varchar NOT NULL DEFAULT ('open'), "resolvedByUserId" varchar, "resolutionNotes" text, "sellerPercent" decimal(5,2), "buyerPercent" decimal(5,2), "outcome" varchar, "resolvedAt" datetime, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "REL_05cc0a36ac22b4dc8a82211d2e" UNIQUE ("escrowId"))`, - ); - await queryRunner.query( - `INSERT INTO "disputes"("id", "escrowId", "filedByUserId", "reason", "evidence", "status", "resolvedByUserId", "resolutionNotes", "sellerPercent", "buyerPercent", "outcome", "resolvedAt", "createdAt", "updatedAt") SELECT "id", "escrowId", "filedByUserId", "reason", "evidence", "status", "resolvedByUserId", "resolutionNotes", "sellerPercent", "buyerPercent", "outcome", "resolvedAt", "createdAt", "updatedAt" FROM "temporary_disputes"`, - ); - await queryRunner.query(`DROP TABLE "temporary_disputes"`); - await queryRunner.query(`ALTER TABLE "users" RENAME TO "temporary_users"`); - await queryRunner.query( - `CREATE TABLE "users" ("id" varchar PRIMARY KEY NOT NULL, "walletAddress" varchar NOT NULL, "nonce" varchar, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "role" varchar CHECK( "role" IN ('USER','ADMIN','SUPER_ADMIN') ) NOT NULL DEFAULT ('USER'), CONSTRAINT "UQ_fc71cd6fb73f95244b23e2ef113" UNIQUE ("walletAddress"))`, - ); - await queryRunner.query( - `INSERT INTO "users"("id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role") SELECT "id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role" FROM "temporary_users"`, - ); - await queryRunner.query(`DROP TABLE "temporary_users"`); - await queryRunner.query(`DROP INDEX "idx_escrows_creator_status_created"`); - await queryRunner.query(`DROP INDEX "idx_escrows_expires_at"`); - await queryRunner.query(`DROP INDEX "idx_escrows_created_at"`); - await queryRunner.query(`DROP INDEX "idx_escrows_asset"`); - await queryRunner.query(`DROP INDEX "idx_escrows_status"`); - await queryRunner.query(`DROP INDEX "idx_escrows_creator"`); - await queryRunner.query( - `ALTER TABLE "escrows" RENAME TO "temporary_escrows"`, - ); - await queryRunner.query( - `CREATE TABLE "escrows" ("id" varchar PRIMARY KEY NOT NULL, "title" varchar NOT NULL, "description" text, "amount" decimal(18,7) NOT NULL, "asset" varchar NOT NULL DEFAULT ('XLM'), "status" varchar NOT NULL DEFAULT ('pending'), "type" varchar NOT NULL DEFAULT ('standard'), "creatorId" varchar NOT NULL, "expiresAt" datetime, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "releaseTransactionHash" varchar, "isReleased" boolean NOT NULL DEFAULT (0), "expirationNotifiedAt" datetime, CONSTRAINT "FK_b483b9f73c28476240bbc993d0b" FOREIGN KEY ("creatorId") REFERENCES "users" ("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`, - ); - await queryRunner.query( - `INSERT INTO "escrows"("id", "title", "description", "amount", "asset", "status", "type", "creatorId", "expiresAt", "isActive", "createdAt", "updatedAt", "releaseTransactionHash", "isReleased", "expirationNotifiedAt") SELECT "id", "title", "description", "amount", "asset", "status", "type", "creatorId", "expiresAt", "isActive", "createdAt", "updatedAt", "releaseTransactionHash", "isReleased", "expirationNotifiedAt" FROM "temporary_escrows"`, - ); - await queryRunner.query(`DROP TABLE "temporary_escrows"`); - await queryRunner.query( - `CREATE INDEX "idx_escrows_creator_status_created" ON "escrows" ("creatorId", "status", "createdAt") `, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_expires_at" ON "escrows" ("expiresAt") `, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_created_at" ON "escrows" ("createdAt") `, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_asset" ON "escrows" ("asset") `, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_status" ON "escrows" ("status") `, - ); - await queryRunner.query( - `CREATE INDEX "idx_escrows_creator" ON "escrows" ("creatorId") `, - ); - await queryRunner.query( - `ALTER TABLE "escrow_conditions" RENAME TO "temporary_escrow_conditions"`, - ); - await queryRunner.query( - `CREATE TABLE "escrow_conditions" ("id" varchar PRIMARY KEY NOT NULL, "escrowId" varchar NOT NULL, "description" text NOT NULL, "type" varchar NOT NULL DEFAULT ('manual'), "isMet" boolean NOT NULL DEFAULT (0), "metAt" datetime, "metByUserId" varchar, "metadata" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), CONSTRAINT "FK_88456ecac834c788fc912233e05" FOREIGN KEY ("escrowId") REFERENCES "escrows" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`, - ); - await queryRunner.query( - `INSERT INTO "escrow_conditions"("id", "escrowId", "description", "type", "isMet", "metAt", "metByUserId", "metadata", "createdAt", "updatedAt") SELECT "id", "escrowId", "description", "type", "isMet", "metAt", "metByUserId", "metadata", "createdAt", "updatedAt" FROM "temporary_escrow_conditions"`, - ); - await queryRunner.query(`DROP TABLE "temporary_escrow_conditions"`); - await queryRunner.query(`DROP INDEX "IDX_f8d00fffa3b5110edf867481dd"`); - await queryRunner.query(`DROP INDEX "IDX_ad3aa47d032fd93d4f307d30ab"`); - await queryRunner.query(`DROP INDEX "IDX_56e937f62e6766e06118ae9b6c"`); - await queryRunner.query(`DROP INDEX "IDX_33bcf3b402d8757d22c1cc1ad3"`); - await queryRunner.query(`DROP TABLE "stellar_events"`); - await queryRunner.query(`DROP TABLE "webhooks"`); - await queryRunner.query(`DROP TABLE "admin_audit_log"`); - await queryRunner.query(`DROP TABLE "api_key"`); - await queryRunner.query(`DROP TABLE "notification_preference"`); - await queryRunner.query(`DROP TABLE "notification"`); - await queryRunner.query(`DROP TABLE "disputes"`); - await queryRunner.query(`ALTER TABLE "users" RENAME TO "temporary_users"`); - await queryRunner.query( - `CREATE TABLE "users" ("id" varchar PRIMARY KEY NOT NULL, "walletAddress" varchar NOT NULL, "nonce" varchar, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "role" varchar CHECK( "role" IN ('USER','ADMIN','SUPER_ADMIN') ) NOT NULL DEFAULT ('USER'), CONSTRAINT "UQ_fc71cd6fb73f95244b23e2ef113" UNIQUE ("walletAddress"))`, - ); - await queryRunner.query( - `INSERT INTO "users"("id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role") SELECT "id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role" FROM "temporary_users"`, - ); - await queryRunner.query(`DROP TABLE "temporary_users"`); - } -} diff --git a/apps/backend/src/migrations/1774364375000-AddReadAtToNotification.ts b/apps/backend/src/migrations/1774364375000-AddReadAtToNotification.ts deleted file mode 100644 index b6081bdd..00000000 --- a/apps/backend/src/migrations/1774364375000-AddReadAtToNotification.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddReadAtToNotification1774364375000 implements MigrationInterface { - name = 'AddReadAtToNotification1774364375000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "notification" ADD COLUMN "readAt" datetime`, - ); - await queryRunner.query( - `ALTER TABLE "notification" ADD COLUMN "escrowId" varchar`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "notification" DROP COLUMN "escrowId"`, - ); - await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "readAt"`); - } -} diff --git a/apps/backend/src/migrations/1774476566443-AddMilestoneProposals.ts b/apps/backend/src/migrations/1774476566443-AddMilestoneProposals.ts deleted file mode 100644 index fb9ef06c..00000000 --- a/apps/backend/src/migrations/1774476566443-AddMilestoneProposals.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddMilestoneProposals1774476566443 implements MigrationInterface { - name = 'AddMilestoneProposals1774476566443'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "temporary_users" ("id" varchar PRIMARY KEY NOT NULL, "walletAddress" varchar NOT NULL, "nonce" varchar, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "role" varchar CHECK( "role" IN ('USER','ADMIN','SUPER_ADMIN') ) NOT NULL DEFAULT ('USER'), CONSTRAINT "UQ_fc71cd6fb73f95244b23e2ef113" UNIQUE ("walletAddress"))`, - ); - await queryRunner.query( - `INSERT INTO "temporary_users"("id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role") SELECT "id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role" FROM "users"`, - ); - await queryRunner.query(`DROP TABLE "users"`); - await queryRunner.query(`ALTER TABLE "temporary_users" RENAME TO "users"`); - await queryRunner.query( - `CREATE TABLE "temporary_escrow_conditions" ("id" varchar PRIMARY KEY NOT NULL, "escrowId" varchar NOT NULL, "description" text NOT NULL, "type" varchar NOT NULL DEFAULT ('manual'), "isMet" boolean NOT NULL DEFAULT (0), "metAt" datetime, "metByUserId" varchar, "metadata" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "isFulfilled" boolean NOT NULL DEFAULT (0), "fulfilledAt" datetime, "fulfilledByUserId" varchar, "fulfillmentNotes" text, "fulfillmentEvidence" text, "amount" decimal(18,7), "proposedAmount" decimal(18,7), "proposedDescription" text, "proposedByUserId" varchar, CONSTRAINT "FK_88456ecac834c788fc912233e05" FOREIGN KEY ("escrowId") REFERENCES "escrows" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`, - ); - await queryRunner.query( - `INSERT INTO "temporary_escrow_conditions"("id", "escrowId", "description", "type", "isMet", "metAt", "metByUserId", "metadata", "createdAt", "updatedAt", "isFulfilled", "fulfilledAt", "fulfilledByUserId", "fulfillmentNotes", "fulfillmentEvidence") SELECT "id", "escrowId", "description", "type", "isMet", "metAt", "metByUserId", "metadata", "createdAt", "updatedAt", "isFulfilled", "fulfilledAt", "fulfilledByUserId", "fulfillmentNotes", "fulfillmentEvidence" FROM "escrow_conditions"`, - ); - await queryRunner.query(`DROP TABLE "escrow_conditions"`); - await queryRunner.query( - `ALTER TABLE "temporary_escrow_conditions" RENAME TO "escrow_conditions"`, - ); - await queryRunner.query( - `CREATE TABLE "temporary_users" ("id" varchar PRIMARY KEY NOT NULL, "walletAddress" varchar NOT NULL, "nonce" varchar, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "role" varchar CHECK( "role" IN ('USER','ADMIN','SUPER_ADMIN') ) NOT NULL DEFAULT ('USER'), CONSTRAINT "UQ_fc71cd6fb73f95244b23e2ef113" UNIQUE ("walletAddress"))`, - ); - await queryRunner.query( - `INSERT INTO "temporary_users"("id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role") SELECT "id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role" FROM "users"`, - ); - await queryRunner.query(`DROP TABLE "users"`); - await queryRunner.query(`ALTER TABLE "temporary_users" RENAME TO "users"`); - await queryRunner.query( - `CREATE TABLE "temporary_notification" ("id" varchar PRIMARY KEY NOT NULL, "userId" varchar NOT NULL, "eventType" varchar CHECK( "eventType" IN ('ESCROW_CREATED','ESCROW_FUNDED','MILESTONE_RELEASED','ESCROW_COMPLETED','ESCROW_CANCELLED','DISPUTE_RAISED','DISPUTE_RESOLVED','ESCROW_EXPIRED','CONDITION_FULFILLED','CONDITION_CONFIRMED','EXPIRATION_WARNING') ) NOT NULL, "payload" text NOT NULL, "status" varchar CHECK( "status" IN ('pending','sent','failed') ) NOT NULL DEFAULT ('pending'), "retryCount" integer NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`, - ); - await queryRunner.query( - `INSERT INTO "temporary_notification"("id", "userId", "eventType", "payload", "status", "retryCount", "createdAt", "updatedAt") SELECT "id", "userId", "eventType", "payload", "status", "retryCount", "createdAt", "updatedAt" FROM "notification"`, - ); - await queryRunner.query(`DROP TABLE "notification"`); - await queryRunner.query( - `ALTER TABLE "temporary_notification" RENAME TO "notification"`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "notification" RENAME TO "temporary_notification"`, - ); - await queryRunner.query( - `CREATE TABLE "notification" ("id" varchar PRIMARY KEY NOT NULL, "userId" varchar NOT NULL, "eventType" varchar CHECK( "eventType" IN ('ESCROW_CREATED','ESCROW_FUNDED','MILESTONE_RELEASED','ESCROW_COMPLETED','ESCROW_CANCELLED','DISPUTE_RAISED','DISPUTE_RESOLVED','ESCROW_EXPIRED') ) NOT NULL, "payload" text NOT NULL, "status" varchar CHECK( "status" IN ('pending','sent','failed') ) NOT NULL DEFAULT ('pending'), "retryCount" integer NOT NULL DEFAULT (0), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')))`, - ); - await queryRunner.query( - `INSERT INTO "notification"("id", "userId", "eventType", "payload", "status", "retryCount", "createdAt", "updatedAt") SELECT "id", "userId", "eventType", "payload", "status", "retryCount", "createdAt", "updatedAt" FROM "temporary_notification"`, - ); - await queryRunner.query(`DROP TABLE "temporary_notification"`); - await queryRunner.query(`ALTER TABLE "users" RENAME TO "temporary_users"`); - await queryRunner.query( - `CREATE TABLE "users" ("id" varchar PRIMARY KEY NOT NULL, "walletAddress" varchar NOT NULL, "nonce" varchar, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "role" varchar CHECK( "role" IN ('USER','ADMIN','SUPER_ADMIN') ) NOT NULL DEFAULT ('USER'), CONSTRAINT "UQ_fc71cd6fb73f95244b23e2ef113" UNIQUE ("walletAddress"))`, - ); - await queryRunner.query( - `INSERT INTO "users"("id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role") SELECT "id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role" FROM "temporary_users"`, - ); - await queryRunner.query(`DROP TABLE "temporary_users"`); - await queryRunner.query( - `ALTER TABLE "escrow_conditions" RENAME TO "temporary_escrow_conditions"`, - ); - await queryRunner.query( - `CREATE TABLE "escrow_conditions" ("id" varchar PRIMARY KEY NOT NULL, "escrowId" varchar NOT NULL, "description" text NOT NULL, "type" varchar NOT NULL DEFAULT ('manual'), "isMet" boolean NOT NULL DEFAULT (0), "metAt" datetime, "metByUserId" varchar, "metadata" text, "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "isFulfilled" boolean NOT NULL DEFAULT (0), "fulfilledAt" datetime, "fulfilledByUserId" varchar, "fulfillmentNotes" text, "fulfillmentEvidence" text, CONSTRAINT "FK_88456ecac834c788fc912233e05" FOREIGN KEY ("escrowId") REFERENCES "escrows" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`, - ); - await queryRunner.query( - `INSERT INTO "escrow_conditions"("id", "escrowId", "description", "type", "isMet", "metAt", "metByUserId", "metadata", "createdAt", "updatedAt", "isFulfilled", "fulfilledAt", "fulfilledByUserId", "fulfillmentNotes", "fulfillmentEvidence") SELECT "id", "escrowId", "description", "type", "isMet", "metAt", "metByUserId", "metadata", "createdAt", "updatedAt", "isFulfilled", "fulfilledAt", "fulfilledByUserId", "fulfillmentNotes", "fulfillmentEvidence" FROM "temporary_escrow_conditions"`, - ); - await queryRunner.query(`DROP TABLE "temporary_escrow_conditions"`); - await queryRunner.query(`ALTER TABLE "users" RENAME TO "temporary_users"`); - await queryRunner.query( - `CREATE TABLE "users" ("id" varchar PRIMARY KEY NOT NULL, "walletAddress" varchar NOT NULL, "nonce" varchar, "isActive" boolean NOT NULL DEFAULT (1), "createdAt" datetime NOT NULL DEFAULT (datetime('now')), "updatedAt" datetime NOT NULL DEFAULT (datetime('now')), "role" varchar CHECK( "role" IN ('USER','ADMIN','SUPER_ADMIN') ) NOT NULL DEFAULT ('USER'), CONSTRAINT "UQ_fc71cd6fb73f95244b23e2ef113" UNIQUE ("walletAddress"))`, - ); - await queryRunner.query( - `INSERT INTO "users"("id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role") SELECT "id", "walletAddress", "nonce", "isActive", "createdAt", "updatedAt", "role" FROM "temporary_users"`, - ); - await queryRunner.query(`DROP TABLE "temporary_users"`); - } -} diff --git a/apps/backend/src/migrations/1780262000000-ImplementFourFeatures.ts b/apps/backend/src/migrations/1780262000000-ImplementFourFeatures.ts deleted file mode 100644 index 39c33611..00000000 --- a/apps/backend/src/migrations/1780262000000-ImplementFourFeatures.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class ImplementFourFeatures1780262000000 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - // Add new fields to User entity - await queryRunner.query( - `ALTER TABLE "users" ADD COLUMN "displayName" varchar(100)`, - ); - await queryRunner.query( - `ALTER TABLE "users" ADD COLUMN "email" varchar(255)`, - ); - await queryRunner.query( - `ALTER TABLE "users" ADD COLUMN "emailVerified" boolean NOT NULL DEFAULT false`, - ); - await queryRunner.query( - `ALTER TABLE "users" ADD COLUMN "avatarUrl" varchar(500)`, - ); - await queryRunner.query(`ALTER TABLE "users" ADD COLUMN "bio" text`); - await queryRunner.query( - `ALTER TABLE "users" ADD COLUMN "preferredAsset" varchar(20) NOT NULL DEFAULT 'XLM'`, - ); - await queryRunner.query( - `CREATE UNIQUE INDEX IF NOT EXISTS "IDX_users_email" ON "users" ("email") WHERE "email" IS NOT NULL`, - ); - - // Create EmailVerification table - await queryRunner.query(` - CREATE TABLE "email_verifications" ( - "id" uuid NOT NULL DEFAULT uuid_generate_v4(), - "userId" uuid NOT NULL, - "token" character varying NOT NULL, - "expiresAt" TIMESTAMP NOT NULL, - "isUsed" boolean NOT NULL DEFAULT false, - "createdAt" TIMESTAMP NOT NULL DEFAULT now(), - CONSTRAINT "PK_email_verifications_id" PRIMARY KEY ("id"), - CONSTRAINT "UQ_email_verifications_token" UNIQUE ("token") - ) - `); - await queryRunner.query( - `ALTER TABLE "email_verifications" ADD CONSTRAINT "FK_email_verifications_userId" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE`, - ); - - // Add new fields to Escrow entity - await queryRunner.query( - `ALTER TABLE "escrows" ADD COLUMN "releasedAmount" numeric(18,7) NOT NULL DEFAULT 0`, - ); - - // Add new fields to Condition entity - await queryRunner.query( - `ALTER TABLE "escrow_conditions" ADD COLUMN "isReleased" boolean NOT NULL DEFAULT false`, - ); - await queryRunner.query( - `ALTER TABLE "escrow_conditions" ADD COLUMN "releasedAt" TIMESTAMP`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - // Drop new columns from Condition entity - await queryRunner.query( - `ALTER TABLE "escrow_conditions" DROP COLUMN "releasedAt"`, - ); - await queryRunner.query( - `ALTER TABLE "escrow_conditions" DROP COLUMN "isReleased"`, - ); - - // Drop new columns from Escrow entity - await queryRunner.query( - `ALTER TABLE "escrows" DROP COLUMN "releasedAmount"`, - ); - - // Drop EmailVerification table - await queryRunner.query(`DROP TABLE "email_verifications"`); - - // Drop new columns from User entity - await queryRunner.query(`DROP INDEX IF EXISTS "IDX_users_email"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "preferredAsset"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "bio"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "avatarUrl"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "emailVerified"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "email"`); - await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "displayName"`); - } -} diff --git a/apps/backend/src/migrations/1780300000000-AddRespondedAtToParty.ts b/apps/backend/src/migrations/1780300000000-AddRespondedAtToParty.ts deleted file mode 100644 index 5aeb91c7..00000000 --- a/apps/backend/src/migrations/1780300000000-AddRespondedAtToParty.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddRespondedAtToParty1780300000000 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "escrow_parties" ADD COLUMN "respondedAt" datetime`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "escrow_parties" DROP COLUMN "respondedAt"`, - ); - } -} diff --git a/apps/backend/src/migrations/1780400000000-AddWebhookDeliveryTable.ts b/apps/backend/src/migrations/1780400000000-AddWebhookDeliveryTable.ts deleted file mode 100644 index b11eaa2b..00000000 --- a/apps/backend/src/migrations/1780400000000-AddWebhookDeliveryTable.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddWebhookDeliveryTable1780400000000 implements MigrationInterface { - name = 'AddWebhookDeliveryTable1780400000000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE TABLE "webhook_delivery" ( - "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4(), - "webhookId" uuid NOT NULL, - "event" varchar NOT NULL, - "payload" text NOT NULL, - "status" varchar NOT NULL DEFAULT 'pending', - "attempt" integer NOT NULL DEFAULT 1, - "maxAttempts" integer NOT NULL DEFAULT 5, - "nextRetryAt" datetime, - "lastStatusCode" integer, - "lastError" text, - "lastAttemptAt" datetime, - "createdAt" datetime DEFAULT now(), - "updatedAt" datetime DEFAULT now(), - CONSTRAINT "FK_webhook_delivery_webhook" FOREIGN KEY ("webhookId") REFERENCES "webhooks"("id") ON DELETE CASCADE - ) - `); - await queryRunner.query( - `CREATE INDEX "idx_webhook_delivery_status_retry" ON "webhook_delivery" ("status", "nextRetryAt")`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `DROP INDEX IF EXISTS "idx_webhook_delivery_status_retry"`, - ); - await queryRunner.query(`DROP TABLE "webhook_delivery"`); - } -} diff --git a/apps/backend/src/migrations/1780500000000-AddEvidenceFilesToDispute.ts b/apps/backend/src/migrations/1780500000000-AddEvidenceFilesToDispute.ts deleted file mode 100644 index 07228283..00000000 --- a/apps/backend/src/migrations/1780500000000-AddEvidenceFilesToDispute.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddEvidenceFilesToDispute1780500000000 implements MigrationInterface { - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "disputes" ADD COLUMN "evidenceFiles" text DEFAULT '[]'`, - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "disputes" DROP COLUMN "evidenceFiles"`, - ); - } -} diff --git a/apps/backend/src/migrations/1780600000000-AddIpfsMetadataToEscrows.ts b/apps/backend/src/migrations/1780600000000-AddIpfsMetadataToEscrows.ts deleted file mode 100644 index 11447a35..00000000 --- a/apps/backend/src/migrations/1780600000000-AddIpfsMetadataToEscrows.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class AddIpfsMetadataToEscrows1780600000000 implements MigrationInterface { - name = 'AddIpfsMetadataToEscrows1780600000000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "escrows" - ADD "ipfs_cid" character varying, - ADD "ipfs_metadata_hash" character varying, - ADD "ipfs_version" integer NOT NULL DEFAULT 0 - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "escrows" - DROP COLUMN "ipfs_version", - DROP COLUMN "ipfs_metadata_hash", - DROP COLUMN "ipfs_cid" - `); - } -} diff --git a/apps/backend/src/modules/admin/admin.controller.ts b/apps/backend/src/modules/admin/admin.controller.ts deleted file mode 100644 index 966b9f91..00000000 --- a/apps/backend/src/modules/admin/admin.controller.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { - Controller, - Get, - Post, - Param, - Query, - UseGuards, - HttpStatus, - HttpCode, -} from '@nestjs/common'; -import { AuthGuard } from '../auth/middleware/auth.guard'; -import { AdminGuard } from '../auth/middleware/admin.guard'; -import { AdminService } from './admin.service'; -import { AdminAuditLogService } from './services/admin-audit-log.service'; -import { EscrowStatus } from '../escrow/entities/escrow.entity'; - -interface AuditLogQuery { - actorId?: string; - actionType?: string; - resourceType?: string; - resourceId?: string; - from?: Date; - to?: Date; - page: number; - pageSize: number; -} - -interface EscrowQuery { - status?: EscrowStatus; - page?: number; - limit?: number; - startDate?: string; - endDate?: string; -} - -interface PaginationQuery { - page?: number; - limit?: number; -} - -@Controller('admin') -@UseGuards(AuthGuard, AdminGuard) -export class AdminController { - constructor( - private readonly adminService: AdminService, - private readonly adminAuditLogService: AdminAuditLogService, - ) {} - - @Get('audit-logs') - async getAuditLogs( - @Query('actorId') actorId?: string, - @Query('actionType') actionType?: string, - @Query('resourceType') resourceType?: string, - @Query('resourceId') resourceId?: string, - @Query('from') from?: string, - @Query('to') to?: string, - @Query('page') page = '1', - @Query('pageSize') pageSize = '20', - ) { - const parsedPage = Number.parseInt(page, 10); - const parsedPageSize = Number.parseInt(pageSize, 10); - - const filters: AuditLogQuery = { - actorId, - actionType, - resourceType, - resourceId, - page: Number.isNaN(parsedPage) ? 1 : parsedPage, - pageSize: Number.isNaN(parsedPageSize) ? 20 : parsedPageSize, - from: from ? new Date(from) : undefined, - to: to ? new Date(to) : undefined, - }; - - return this.adminAuditLogService.findAll(filters); - } - - @Get('escrows') - async getAllEscrows(@Query() query: EscrowQuery) { - return this.adminService.getAllEscrows(query); - } - - @Get('users') - async getAllUsers(@Query() query: PaginationQuery) { - const page = query.page ?? 1; - const limit = query.limit ?? 20; - - return this.adminService.getAllUsers(page, limit); - } - - @Get('stats') - async getStats() { - return this.adminService.getPlatformStats(); - } - - @Post('users/:id/suspend') - @HttpCode(HttpStatus.OK) - async suspendUser( - @Param('id') id: string, - @Query('actorId') actorId?: string, - ) { - return this.adminService.suspendUser(id, actorId); - } -} diff --git a/apps/backend/src/modules/admin/admin.module.ts b/apps/backend/src/modules/admin/admin.module.ts deleted file mode 100644 index a82059a0..00000000 --- a/apps/backend/src/modules/admin/admin.module.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Module, forwardRef } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; - -import { AdminController } from './admin.controller'; -import { AdminService } from './admin.service'; -import { User } from '../user/entities/user.entity'; -import { Escrow } from '../escrow/entities/escrow.entity'; -import { Party } from '../escrow/entities/party.entity'; -import { EscrowEvent } from '../escrow/entities/escrow-event.entity'; -import { AuthModule } from '../auth/auth.module'; -import { EscrowModule } from '../escrow/escrow.module'; -import { ConsistencyCheckerService } from './services/consistency-checker.service'; -import { AdminEscrowConsistencyController } from './controllers/admin-escrow-consistency.controller'; -import { AdminAuditLog } from './entities/admin-audit-log.entity'; -import { AdminAuditLogService } from './services/admin-audit-log.service'; -import { AnalyticsService } from './services/analytics.service'; -import { AnalyticsController } from './controllers/analytics.controller'; -import { Dispute } from '../escrow/entities/dispute.entity'; -import { WebhookModule } from '../webhook/webhook.module'; -import { AdminWebhookController } from './controllers/admin-webhook.controller'; - -@Module({ - imports: [ - AuthModule, - TypeOrmModule.forFeature([ - User, - Escrow, - Party, - EscrowEvent, - AdminAuditLog, - Dispute, - ]), - EscrowModule, - forwardRef(() => WebhookModule), - ], - controllers: [ - AdminController, - AdminEscrowConsistencyController, - AnalyticsController, - AdminWebhookController, - ], - providers: [ - AdminService, - ConsistencyCheckerService, - AdminAuditLogService, - AnalyticsService, - ], - exports: [AdminService, ConsistencyCheckerService], -}) -export class AdminModule {} diff --git a/apps/backend/src/modules/admin/admin.service.spec.ts b/apps/backend/src/modules/admin/admin.service.spec.ts deleted file mode 100644 index cf0212f9..00000000 --- a/apps/backend/src/modules/admin/admin.service.spec.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AdminService } from './admin.service'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { User, UserRole } from '../user/entities/user.entity'; -import { Escrow, EscrowStatus } from '../escrow/entities/escrow.entity'; -import { Party } from '../escrow/entities/party.entity'; -import { EscrowEvent } from '../escrow/entities/escrow-event.entity'; -import { AdminAuditLogService } from './services/admin-audit-log.service'; -import { Repository } from 'typeorm'; - -describe('AdminService', () => { - let service: AdminService; - let userRepo: jest.Mocked; - let escrowRepo: jest.Mocked; - let auditLogService: jest.Mocked; - - beforeEach(async () => { - userRepo = { - findAndCount: jest - .fn() - .mockResolvedValue([ - [{ id: 'u1', isActive: true, role: UserRole.USER }], - 1, - ]), - count: jest.fn().mockResolvedValue(1), - findOne: jest.fn(), - save: jest.fn(), - createQueryBuilder: jest.fn().mockReturnValue({ - select: jest.fn().mockReturnThis(), - addSelect: jest.fn().mockReturnThis(), - groupBy: jest.fn().mockReturnThis(), - getRawMany: jest - .fn() - .mockResolvedValue([{ role: UserRole.USER, count: '1' }]), - }), - }; - - escrowRepo = { - findAndCount: jest.fn().mockResolvedValue([[], 0]), - count: jest.fn().mockResolvedValue(0), - createQueryBuilder: jest.fn().mockReturnValue({ - select: jest.fn().mockReturnThis(), - where: jest.fn().mockReturnThis(), - getRawOne: jest.fn().mockResolvedValue({ total: '100' }), - }), - }; - - const module: TestingModule = await Test.createTestingModule({ - providers: [ - AdminService, - { - provide: getRepositoryToken(User), - useValue: userRepo, - }, - { - provide: getRepositoryToken(Escrow), - useValue: escrowRepo, - }, - { - provide: getRepositoryToken(Party), - useValue: {}, - }, - { - provide: getRepositoryToken(EscrowEvent), - useValue: {}, - }, - { - provide: AdminAuditLogService, - useValue: { - create: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(AdminService); - auditLogService = module.get(AdminAuditLogService); - }); - - describe('getAllUsers', () => { - it('should return paginated users', async () => { - const result = await service.getAllUsers(1, 10); - expect(result.users).toHaveLength(1); - expect(result.pagination.total).toBe(1); - }); - }); - - describe('getAllEscrows', () => { - it('should return paginated escrows with filters', async () => { - const result = await service.getAllEscrows({ - status: EscrowStatus.ACTIVE, - }); - expect(escrowRepo.findAndCount).toHaveBeenCalledWith( - expect.objectContaining({ - where: { status: EscrowStatus.ACTIVE }, - }), - ); - expect(result.escrows).toHaveLength(0); - }); - }); - - describe('getPlatformStats', () => { - it('should aggregate various platform stats', async () => { - const result = await service.getPlatformStats(); - expect(result).toHaveProperty('users'); - expect(result).toHaveProperty('escrows'); - expect(result).toHaveProperty('volume'); - expect(result.volume.totalCompleted).toBe(100); - }); - }); - - describe('suspendUser', () => { - it('should suspend a normal user', async () => { - const mockUser = { id: 'u1', role: UserRole.USER, isActive: true }; - userRepo.findOne.mockResolvedValue(mockUser); - - const result = await service.suspendUser('u1', 'admin-id'); - - expect(mockUser.isActive).toBe(false); - expect(userRepo.save).toHaveBeenCalled(); - expect(auditLogService.create).toHaveBeenCalled(); - expect(result.message).toBe('User suspended successfully'); - }); - - it('should throw if user not found', async () => { - userRepo.findOne.mockResolvedValue(null); - await expect(service.suspendUser('u1')).rejects.toThrow('User not found'); - }); - - it('should throw if user is super admin', async () => { - const superAdmin = { id: 's1', role: UserRole.SUPER_ADMIN }; - userRepo.findOne.mockResolvedValue(superAdmin); - await expect(service.suspendUser('s1')).rejects.toThrow( - 'Cannot suspend super admin', - ); - }); - }); -}); diff --git a/apps/backend/src/modules/admin/admin.service.ts b/apps/backend/src/modules/admin/admin.service.ts deleted file mode 100644 index ad36a04f..00000000 --- a/apps/backend/src/modules/admin/admin.service.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { Injectable, Inject, forwardRef } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, Between, MoreThan, LessThan } from 'typeorm'; -import { User, UserRole } from '../user/entities/user.entity'; -import { AdminAuditLogService } from './services/admin-audit-log.service'; -import { Escrow, EscrowStatus } from '../escrow/entities/escrow.entity'; -import { Party } from '../escrow/entities/party.entity'; -import { EscrowEvent } from '../escrow/entities/escrow-event.entity'; - -@Injectable() -export class AdminService { - constructor( - @InjectRepository(User) - private userRepository: Repository, - @InjectRepository(Escrow) - private escrowRepository: Repository, - @InjectRepository(Party) - private partyRepository: Repository, - @InjectRepository(EscrowEvent) - private escrowEventRepository: Repository, - private readonly adminAuditLogService: AdminAuditLogService, - ) {} - - async getAllUsers(page: number = 1, limit: number = 50) { - const [users, total] = await this.userRepository.findAndCount({ - skip: (page - 1) * limit, - take: limit, - order: { createdAt: 'DESC' }, - select: [ - 'id', - 'walletAddress', - 'role', - 'isActive', - 'createdAt', - 'updatedAt', - ], - }); - - return { - users, - pagination: { - page, - limit, - total, - pages: Math.ceil(total / limit), - }, - }; - } - - async getAllEscrows(filters: { - status?: EscrowStatus; - page?: number; - limit?: number; - startDate?: string; - endDate?: string; - }) { - const { status, page = 1, limit = 50, startDate, endDate } = filters; - - const where: any = {}; - if (status) where.status = status; - if (startDate && endDate) { - where.createdAt = Between(new Date(startDate), new Date(endDate)); - } - - const [escrows, total] = await this.escrowRepository.findAndCount({ - where, - skip: (page - 1) * limit, - take: limit, - order: { createdAt: 'DESC' }, - relations: ['parties'], - }); - - return { - escrows, - pagination: { - page, - limit, - total, - pages: Math.ceil(total / limit), - }, - }; - } - - async getPlatformStats() { - const [ - totalUsers, - activeUsers, - totalEscrows, - activeEscrows, - completedEscrows, - totalVolume, - ] = await Promise.all([ - this.userRepository.count(), - this.userRepository.count({ where: { isActive: true } }), - this.escrowRepository.count(), - this.escrowRepository.count({ where: { status: EscrowStatus.ACTIVE } }), - this.escrowRepository.count({ - where: { status: EscrowStatus.COMPLETED }, - }), - this.escrowRepository - .createQueryBuilder('escrow') - .select('SUM(escrow.amount)', 'total') - .where('escrow.status = :status', { status: EscrowStatus.COMPLETED }) - .getRawOne(), - ]); - - const last30Days = new Date(); - last30Days.setDate(last30Days.getDate() - 30); - - const [ - newUsersLast30Days, - newEscrowsLast30Days, - completedEscrowsLast30Days, - ] = await Promise.all([ - this.userRepository.count({ - where: { createdAt: MoreThan(last30Days) }, - }), - this.escrowRepository.count({ - where: { createdAt: MoreThan(last30Days) }, - }), - this.escrowRepository.count({ - where: { - status: EscrowStatus.COMPLETED, - updatedAt: MoreThan(last30Days), - }, - }), - ]); - - return { - users: { - total: totalUsers, - active: activeUsers, - newLast30Days: newUsersLast30Days, - }, - escrows: { - total: totalEscrows, - active: activeEscrows, - completed: completedEscrows, - newLast30Days: newEscrowsLast30Days, - completedLast30Days: completedEscrowsLast30Days, - }, - volume: { - totalCompleted: parseFloat(totalVolume?.total || '0'), - }, - roles: await this.getUserRoleStats(), - }; - } - - async suspendUser(userId: string, actorId?: string) { - const user = await this.userRepository.findOne({ where: { id: userId } }); - - if (!user) { - throw new Error('User not found'); - } - - if (user.role === UserRole.SUPER_ADMIN) { - throw new Error('Cannot suspend super admin'); - } - - const oldStatus = user.isActive; - user.isActive = false; - await this.userRepository.save(user); - - // Audit log - await this.adminAuditLogService.create({ - actorId: actorId || 'system', - actionType: 'SUSPEND_USER', - resourceType: 'USER', - resourceId: user.id, - metadata: { - oldStatus, - newStatus: user.isActive, - userRole: user.role, - }, - }); - - return { message: 'User suspended successfully', user }; - } - - private async getUserRoleStats() { - const stats = await this.userRepository - .createQueryBuilder('user') - .select('user.role', 'role') - .addSelect('COUNT(*)', 'count') - .groupBy('user.role') - .getRawMany(); - - return stats.reduce((acc, stat) => { - acc[stat.role] = parseInt(stat.count); - return acc; - }, {}); - } -} diff --git a/apps/backend/src/modules/admin/controllers/admin-audit-log.controller.spec.ts b/apps/backend/src/modules/admin/controllers/admin-audit-log.controller.spec.ts deleted file mode 100644 index 3bade919..00000000 --- a/apps/backend/src/modules/admin/controllers/admin-audit-log.controller.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AdminController } from '../admin.controller'; -import { AdminService } from '../admin.service'; -import { AdminAuditLogService } from '../services/admin-audit-log.service'; -import { AdminGuard } from '../../auth/middleware/admin.guard'; -import { AuthGuard } from '../../auth/middleware/auth.guard'; - -describe('AdminController (audit log endpoint)', () => { - let controller: AdminController; - let auditLogService: AdminAuditLogService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AdminController], - providers: [ - { - provide: AdminService, - useValue: {}, - }, - { - provide: AdminAuditLogService, - useValue: { - findAll: jest.fn().mockResolvedValue({ data: [], total: 0 }), - }, - }, - ], - }) - .overrideGuard(AuthGuard) - .useValue({ canActivate: () => true }) - .overrideGuard(AdminGuard) - .useValue({ canActivate: () => true }) - .compile(); - - controller = module.get(AdminController); - auditLogService = module.get(AdminAuditLogService); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); - - it('should call auditLogService.findAll with filters', async () => { - const spy = jest.spyOn(auditLogService, 'findAll'); - await controller.getAuditLogs( - 'admin-1', - 'SUSPEND_USER', - 'USER', - 'user-123', - undefined, - undefined, - '1', - '10', - ); - expect(spy).toHaveBeenCalledWith({ - actorId: 'admin-1', - actionType: 'SUSPEND_USER', - resourceType: 'USER', - resourceId: 'user-123', - page: 1, - pageSize: 10, - from: undefined, - to: undefined, - }); - }); -}); diff --git a/apps/backend/src/modules/admin/controllers/admin-escrow-consistency.controller.spec.ts b/apps/backend/src/modules/admin/controllers/admin-escrow-consistency.controller.spec.ts deleted file mode 100644 index 99add2de..00000000 --- a/apps/backend/src/modules/admin/controllers/admin-escrow-consistency.controller.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AdminEscrowConsistencyController } from './admin-escrow-consistency.controller'; -import { ConsistencyCheckerService } from '../services/consistency-checker.service'; - -describe('AdminEscrowConsistencyController', () => { - let controller: AdminEscrowConsistencyController; - let checkerService: jest.Mocked; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [AdminEscrowConsistencyController], - providers: [ - { - provide: ConsistencyCheckerService, - useValue: { - checkConsistency: jest.fn(), - }, - }, - ], - }).compile(); - - controller = module.get( - AdminEscrowConsistencyController, - ); - checkerService = module.get(ConsistencyCheckerService); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); - - it('should call checkerService.checkConsistency', async () => { - const mockResult = { - reports: [], - summary: { - totalChecked: 1, - totalInconsistent: 0, - totalMissingInDb: 0, - totalMissingOnChain: 0, - totalErrored: 0, - }, - }; - const spy = jest - .spyOn(checkerService, 'checkConsistency') - .mockResolvedValueOnce(mockResult); - - const result = await controller.checkConsistency({ escrowIds: [1] }); - expect(result.summary.totalChecked).toBe(1); - expect(spy).toHaveBeenCalledWith({ escrowIds: [1] }); - }); -}); diff --git a/apps/backend/src/modules/admin/controllers/admin-escrow-consistency.controller.ts b/apps/backend/src/modules/admin/controllers/admin-escrow-consistency.controller.ts deleted file mode 100644 index 87a92072..00000000 --- a/apps/backend/src/modules/admin/controllers/admin-escrow-consistency.controller.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Body, Controller, Post, UseGuards } from '@nestjs/common'; -import { ConsistencyCheckerService } from '../services/consistency-checker.service'; -import { - ConsistencyCheckRequest, - ConsistencyCheckResponse, -} from '../dto/consistency-check.dto'; -import { AdminGuard } from '../../auth/middleware/admin.guard'; - -@Controller('admin/escrows') -@UseGuards(AdminGuard) -export class AdminEscrowConsistencyController { - constructor(private readonly checker: ConsistencyCheckerService) {} - - @Post('consistency-check') - async checkConsistency( - @Body() body: ConsistencyCheckRequest, - ): Promise { - return this.checker.checkConsistency(body); - } -} diff --git a/apps/backend/src/modules/admin/controllers/admin-webhook.controller.ts b/apps/backend/src/modules/admin/controllers/admin-webhook.controller.ts deleted file mode 100644 index e59b9ce9..00000000 --- a/apps/backend/src/modules/admin/controllers/admin-webhook.controller.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { - Controller, - Get, - Post, - Param, - Query, - UseGuards, - HttpStatus, - HttpCode, -} from '@nestjs/common'; -import { AuthGuard } from '../../auth/middleware/auth.guard'; -import { AdminGuard } from '../../auth/middleware/admin.guard'; -import { WebhookService } from '../../../services/webhook/webhook.service'; - -@Controller('admin/webhooks') -@UseGuards(AuthGuard, AdminGuard) -export class AdminWebhookController { - constructor(private readonly webhookService: WebhookService) {} - - @Get('failed') - async getFailedDeliveries( - @Query('page') page = '1', - @Query('limit') limit = '20', - @Query('webhookId') webhookId?: string, - ) { - const parsedPage = Number.parseInt(page, 10); - const parsedLimit = Number.parseInt(limit, 10); - - return this.webhookService.getFailedDelveys({ - page: Number.isNaN(parsedPage) ? 1 : parsedPage, - limit: Number.isNaN(parsedLimit) ? 20 : parsedLimit, - webhookId, - }); - } - - @Post(':deliveryId/retry') - @HttpCode(HttpStatus.OK) - async manualRetry(@Param('deliveryId') deliveryId: string) { - return this.webhookService.manualRetry(deliveryId); - } - - @Get('health') - async getHealthStats() { - return this.webhookService.getHealthStats(); - } -} diff --git a/apps/backend/src/modules/admin/controllers/analytics.controller.ts b/apps/backend/src/modules/admin/controllers/analytics.controller.ts deleted file mode 100644 index 0fb92fec..00000000 --- a/apps/backend/src/modules/admin/controllers/analytics.controller.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Controller, Get, Query, Res, UseGuards } from '@nestjs/common'; -import { ApiTags, ApiOperation, ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; -import { Response } from 'express'; -import { AuthGuard } from '../../auth/middleware/auth.guard'; -import { AdminGuard } from '../../auth/middleware/admin.guard'; -import { AnalyticsService } from '../services/analytics.service'; - -@ApiTags('admin/analytics') -@ApiBearerAuth() -@UseGuards(AuthGuard, AdminGuard) -@Controller('admin/analytics') -export class AnalyticsController { - constructor(private readonly analyticsService: AnalyticsService) {} - - @Get('summary') - @ApiOperation({ - summary: 'Get full analytics summary (volume, activity, performance, users, disputes)', - }) - async getSummary(@Res({ passthrough: true }) res: Response) { - res.setHeader('Cache-Control', 'public, max-age=300'); - return this.analyticsService.getSummary(); - } - - @Get('volume') - @ApiOperation({ summary: 'Get escrow volume time-series data' }) - @ApiQuery({ name: 'period', required: false, enum: ['daily', 'weekly', 'monthly'] }) - @ApiQuery({ name: 'from', required: false, type: String }) - @ApiQuery({ name: 'to', required: false, type: String }) - async getVolume( - @Res({ passthrough: true }) res: Response, - @Query('period') period: 'daily' | 'weekly' | 'monthly' = 'daily', - @Query('from') from?: string, - @Query('to') to?: string, - ) { - res.setHeader('Cache-Control', 'public, max-age=900'); - const [series, timeSeries] = await Promise.all([ - this.analyticsService.getVolumeStats(period, from, to), - this.analyticsService.getVolumeTimeSeries(from, to), - ]); - return { series, charts: timeSeries }; - } - - @Get('users') - @ApiOperation({ summary: 'Get user metrics and weekly active user time-series' }) - async getUsers(@Res({ passthrough: true }) res: Response) { - res.setHeader('Cache-Control', 'public, max-age=300'); - const [metrics, timeSeries] = await Promise.all([ - this.analyticsService.getUserMetrics(), - this.analyticsService.getUserTimeSeries(), - ]); - return { metrics, charts: timeSeries }; - } - - @Get('disputes') - @ApiOperation({ summary: 'Get dispute metrics including resolution and win rates' }) - async getDisputes(@Res({ passthrough: true }) res: Response) { - res.setHeader('Cache-Control', 'public, max-age=300'); - return this.analyticsService.getDisputeMetrics(); - } - - @Get('top-users') - @ApiOperation({ summary: 'Get leaderboard of top users by volume' }) - @ApiQuery({ name: 'limit', required: false, type: Number }) - async getTopUsers( - @Res({ passthrough: true }) res: Response, - @Query('limit') limit: string = '10', - ) { - res.setHeader('Cache-Control', 'public, max-age=300'); - return this.analyticsService.getTopUsers(parseInt(limit, 10)); - } -} diff --git a/apps/backend/src/modules/admin/dto/consistency-check.dto.ts b/apps/backend/src/modules/admin/dto/consistency-check.dto.ts deleted file mode 100644 index 6163ca9b..00000000 --- a/apps/backend/src/modules/admin/dto/consistency-check.dto.ts +++ /dev/null @@ -1,31 +0,0 @@ -// DTOs and types for the Consistency Checker feature - -export type ConsistencyCheckRequest = - | { escrowIds: number[] } - | { fromId: number; toId: number }; - -export interface FieldMismatch { - fieldName: string; - dbValue: unknown; - onchainValue: unknown; -} - -export interface EscrowDiffReport { - escrowId: number; - isConsistent: boolean; - fieldsMismatched: FieldMismatch[]; - missingInDb?: boolean; - missingOnChain?: boolean; - error?: string; -} - -export interface ConsistencyCheckResponse { - reports: EscrowDiffReport[]; - summary: { - totalChecked: number; - totalInconsistent: number; - totalMissingInDb: number; - totalMissingOnChain: number; - totalErrored: number; - }; -} diff --git a/apps/backend/src/modules/admin/entities/admin-audit-log.entity.ts b/apps/backend/src/modules/admin/entities/admin-audit-log.entity.ts deleted file mode 100644 index bc78b1d2..00000000 --- a/apps/backend/src/modules/admin/entities/admin-audit-log.entity.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { - Column, - CreateDateColumn, - Entity, - PrimaryGeneratedColumn, -} from 'typeorm'; - -@Entity('admin_audit_log') -export class AdminAuditLog { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column({ type: 'varchar', length: 64 }) - actorId: string; - - @Column({ type: 'varchar', length: 64 }) - actionType: string; - - @Column({ type: 'varchar', length: 64 }) - resourceType: string; - - @Column({ type: 'varchar', length: 128, nullable: true }) - resourceId: string | null; - - @Column({ type: 'simple-json', nullable: true }) - metadata?: Record; - - @CreateDateColumn() - createdAt: Date; -} diff --git a/apps/backend/src/modules/admin/services/admin-audit-log.service.ts b/apps/backend/src/modules/admin/services/admin-audit-log.service.ts deleted file mode 100644 index 4e6a73fa..00000000 --- a/apps/backend/src/modules/admin/services/admin-audit-log.service.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { - Repository, - FindManyOptions, - FindOptionsWhere, - Between, -} from 'typeorm'; -import { AdminAuditLog } from '../entities/admin-audit-log.entity'; - -export interface CreateAuditLogDto { - actorId: string; - actionType: string; - resourceType: string; - resourceId?: string | null; - metadata?: Record; -} - -export interface AuditLogFilters { - actorId?: string; - actionType?: string; - resourceType?: string; - resourceId?: string; - from?: Date; - to?: Date; - page?: number; - pageSize?: number; -} - -@Injectable() -export class AdminAuditLogService { - constructor( - @InjectRepository(AdminAuditLog) - private readonly auditLogRepo: Repository, - ) {} - - async create(dto: CreateAuditLogDto): Promise { - const log = this.auditLogRepo.create({ - ...dto, - resourceId: dto.resourceId ?? null, - metadata: dto.metadata ?? {}, - }); - return this.auditLogRepo.save(log); - } - - async findAll( - filters: AuditLogFilters = {}, - ): Promise<{ data: AdminAuditLog[]; total: number }> { - const { - actorId, - actionType, - resourceType, - resourceId, - from, - to, - page = 1, - pageSize = 20, - } = filters; - - const where: FindOptionsWhere = {}; - if (actorId) where.actorId = actorId; - if (actionType) where.actionType = actionType; - if (resourceType) where.resourceType = resourceType; - if (resourceId) where.resourceId = resourceId; - if (from && to) { - where.createdAt = Between(from, to); - } else if (from) { - where.createdAt = Between(from, new Date()); - } else if (to) { - where.createdAt = Between(new Date(0), to); - } - - const [data, total] = await this.auditLogRepo.findAndCount({ - where, - order: { createdAt: 'DESC' }, - skip: (page - 1) * pageSize, - take: pageSize, - } as FindManyOptions); - return { data, total }; - } -} diff --git a/apps/backend/src/modules/admin/services/analytics.service.ts b/apps/backend/src/modules/admin/services/analytics.service.ts deleted file mode 100644 index 3d060c18..00000000 --- a/apps/backend/src/modules/admin/services/analytics.service.ts +++ /dev/null @@ -1,492 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, MoreThan } from 'typeorm'; -import { Escrow, EscrowStatus } from '../../escrow/entities/escrow.entity'; -import { Dispute, DisputeStatus, DisputeOutcome } from '../../escrow/entities/dispute.entity'; -import { User } from '../../user/entities/user.entity'; - -export interface ChartData { - labels: string[]; - values: number[]; -} - -export interface VolumeMetrics { - totalVolume: number; - averageAmount: number; - medianAmount: number; -} - -export interface ActivityMetrics { - created: number; - active: number; - completed: number; - disputed: number; - cancelled: number; - refunded: number; - expired: number; - total: number; -} - -export interface PerformanceMetrics { - avgTimeToFundingDays: number; - avgTimeToCompletionDays: number; - avgDisputeResolutionDays: number; -} - -export interface UserMetrics { - total: number; - active30d: number; - new7d: number; - new30d: number; - new90d: number; -} - -export interface DisputeMetrics { - totalDisputes: number; - disputeRate: number; - resolutionRate: number; - avgResolutionTimeDays: number; - winRate: number; - outcomeDistribution: Record; -} - -export interface SummaryAnalytics { - volume: VolumeMetrics; - activity: ActivityMetrics; - performance: PerformanceMetrics; - users: UserMetrics; - disputes: DisputeMetrics; - generatedAt: string; -} - -export interface VolumeStat { - period: string; - count: number; - volume: number; -} - -export interface VolumeTimeSeries { - daily30d: ChartData; - daily90d: ChartData; - monthly12m: ChartData; -} - -export interface UserTimeSeries { - weeklyActive12w: ChartData; -} - -export interface TopUser { - walletAddress: string; - escrowCount: number; - totalVolume: number; - completionRate: number; -} - -@Injectable() -export class AnalyticsService { - private cache = new Map(); - private readonly SUMMARY_TTL = 5 * 60 * 1000; - private readonly TIMESERIES_TTL = 15 * 60 * 1000; - - constructor( - @InjectRepository(Escrow) - private escrowRepository: Repository, - @InjectRepository(Dispute) - private disputeRepository: Repository, - @InjectRepository(User) - private userRepository: Repository, - ) {} - - private getFromCache(key: string): T | null { - const cached = this.cache.get(key); - if (cached && cached.expiry > Date.now()) { - return cached.data as T; - } - return null; - } - - private setCache(key: string, data: unknown, ttl: number): void { - this.cache.set(key, { data, expiry: Date.now() + ttl }); - } - - private daysAgo(days: number): Date { - const d = new Date(); - d.setDate(d.getDate() - days); - return d; - } - - async getSummary(): Promise { - const cacheKey = 'analytics_summary'; - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - const [volume, activity, performance, users, disputes] = await Promise.all([ - this.getVolumeMetrics(), - this.getActivityMetrics(), - this.getPerformanceMetrics(), - this.getUserMetrics(), - this.getDisputeMetrics(), - ]); - - const summary: SummaryAnalytics = { - volume, - activity, - performance, - users, - disputes, - generatedAt: new Date().toISOString(), - }; - - this.setCache(cacheKey, summary, this.SUMMARY_TTL); - return summary; - } - - async getVolumeMetrics(): Promise { - const cacheKey = 'analytics_volume_metrics'; - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - const rows = await this.escrowRepository - .createQueryBuilder('escrow') - .select('escrow.amount', 'amount') - .where('escrow.status = :status', { status: EscrowStatus.COMPLETED }) - .getRawMany<{ amount: string }>(); - - const amounts = rows.map((r) => parseFloat(r.amount || '0')).sort((a, b) => a - b); - const total = amounts.reduce((sum, v) => sum + v, 0); - const avg = amounts.length > 0 ? total / amounts.length : 0; - - let median = 0; - if (amounts.length > 0) { - const mid = Math.floor(amounts.length / 2); - median = - amounts.length % 2 !== 0 - ? amounts[mid] - : (amounts[mid - 1] + amounts[mid]) / 2; - } - - const metrics: VolumeMetrics = { - totalVolume: parseFloat(total.toFixed(7)), - averageAmount: parseFloat(avg.toFixed(7)), - medianAmount: parseFloat(median.toFixed(7)), - }; - - this.setCache(cacheKey, metrics, this.SUMMARY_TTL); - return metrics; - } - - async getActivityMetrics(): Promise { - const cacheKey = 'analytics_activity'; - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - const rows = await this.escrowRepository - .createQueryBuilder('escrow') - .select('escrow.status', 'status') - .addSelect('COUNT(*)', 'count') - .groupBy('escrow.status') - .getRawMany<{ status: string; count: string }>(); - - const counts = rows.reduce>((acc, r) => { - acc[r.status] = parseInt(r.count); - return acc; - }, {}); - - const metrics: ActivityMetrics = { - created: counts[EscrowStatus.PENDING] ?? 0, - active: counts[EscrowStatus.ACTIVE] ?? 0, - completed: counts[EscrowStatus.COMPLETED] ?? 0, - disputed: counts[EscrowStatus.DISPUTED] ?? 0, - cancelled: counts[EscrowStatus.CANCELLED] ?? 0, - refunded: counts[EscrowStatus.CANCELLED] ?? 0, - expired: counts[EscrowStatus.EXPIRED] ?? 0, - total: rows.reduce((sum, r) => sum + parseInt(r.count), 0), - }; - - this.setCache(cacheKey, metrics, this.SUMMARY_TTL); - return metrics; - } - - async getPerformanceMetrics(): Promise { - const cacheKey = 'analytics_performance'; - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - const [fundingRaw, completionRaw, disputeRaw] = await Promise.all([ - this.escrowRepository - .createQueryBuilder('escrow') - .select( - 'AVG(julianday(escrow.fundedAt) - julianday(escrow.createdAt))', - 'avgDays', - ) - .where('escrow.fundedAt IS NOT NULL') - .getRawOne<{ avgDays: string | null }>(), - this.escrowRepository - .createQueryBuilder('escrow') - .select( - 'AVG(julianday(escrow.updatedAt) - julianday(escrow.createdAt))', - 'avgDays', - ) - .where('escrow.status = :status', { status: EscrowStatus.COMPLETED }) - .getRawOne<{ avgDays: string | null }>(), - this.disputeRepository - .createQueryBuilder('dispute') - .select( - 'AVG(julianday(dispute.resolvedAt) - julianday(dispute.createdAt))', - 'avgDays', - ) - .where('dispute.status = :status', { status: DisputeStatus.RESOLVED }) - .getRawOne<{ avgDays: string | null }>(), - ]); - - const metrics: PerformanceMetrics = { - avgTimeToFundingDays: parseFloat( - parseFloat(fundingRaw?.avgDays || '0').toFixed(2), - ), - avgTimeToCompletionDays: parseFloat( - parseFloat(completionRaw?.avgDays || '0').toFixed(2), - ), - avgDisputeResolutionDays: parseFloat( - parseFloat(disputeRaw?.avgDays || '0').toFixed(2), - ), - }; - - this.setCache(cacheKey, metrics, this.SUMMARY_TTL); - return metrics; - } - - async getUserMetrics(): Promise { - const cacheKey = 'analytics_users'; - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - const [total, active30d, new7d, new30d, new90d] = await Promise.all([ - this.userRepository.count(), - this.userRepository.count({ where: { updatedAt: MoreThan(this.daysAgo(30)) } }), - this.userRepository.count({ where: { createdAt: MoreThan(this.daysAgo(7)) } }), - this.userRepository.count({ where: { createdAt: MoreThan(this.daysAgo(30)) } }), - this.userRepository.count({ where: { createdAt: MoreThan(this.daysAgo(90)) } }), - ]); - - const metrics: UserMetrics = { total, active30d, new7d, new30d, new90d }; - this.setCache(cacheKey, metrics, this.SUMMARY_TTL); - return metrics; - } - - async getDisputeMetrics(): Promise { - const cacheKey = 'analytics_disputes'; - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - const [totalEscrows, totalDisputes, resolvedCount, outcomes, avgResolutionRaw, buyerWins] = - await Promise.all([ - this.escrowRepository.count(), - this.disputeRepository.count(), - this.disputeRepository.count({ where: { status: DisputeStatus.RESOLVED } }), - this.disputeRepository - .createQueryBuilder('dispute') - .select('dispute.outcome', 'outcome') - .addSelect('COUNT(*)', 'count') - .where('dispute.status = :status', { status: DisputeStatus.RESOLVED }) - .groupBy('dispute.outcome') - .getRawMany<{ outcome: string | null; count: string }>(), - this.disputeRepository - .createQueryBuilder('dispute') - .select( - 'AVG(julianday(dispute.resolvedAt) - julianday(dispute.createdAt))', - 'avgDays', - ) - .where('dispute.status = :status', { status: DisputeStatus.RESOLVED }) - .getRawOne<{ avgDays: string | null }>(), - this.disputeRepository.count({ - where: { status: DisputeStatus.RESOLVED, outcome: DisputeOutcome.REFUNDED_TO_BUYER }, - }), - ]); - - const disputeRate = totalEscrows > 0 ? (totalDisputes / totalEscrows) * 100 : 0; - const resolutionRate = totalDisputes > 0 ? (resolvedCount / totalDisputes) * 100 : 0; - const winRate = resolvedCount > 0 ? (buyerWins / resolvedCount) * 100 : 0; - - const metrics: DisputeMetrics = { - totalDisputes, - disputeRate: parseFloat(disputeRate.toFixed(2)), - resolutionRate: parseFloat(resolutionRate.toFixed(2)), - avgResolutionTimeDays: parseFloat( - parseFloat(avgResolutionRaw?.avgDays || '0').toFixed(2), - ), - winRate: parseFloat(winRate.toFixed(2)), - outcomeDistribution: outcomes.reduce>( - (acc, curr) => { - if (curr.outcome) acc[curr.outcome] = parseInt(curr.count); - return acc; - }, - {}, - ), - }; - - this.setCache(cacheKey, metrics, this.SUMMARY_TTL); - return metrics; - } - - async getVolumeTimeSeries(from?: string, to?: string): Promise { - const cacheKey = `analytics_volume_ts_${from}_${to}`; - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - const [daily30, daily90, monthly12] = await Promise.all([ - this.queryVolumeSeries('%Y-%m-%d', this.daysAgo(30), from, to), - this.queryVolumeSeries('%Y-%m-%d', this.daysAgo(90), from, to), - this.queryVolumeSeries('%Y-%m', this.daysAgo(365), from, to), - ]); - - const result: VolumeTimeSeries = { - daily30d: this.toChartData(daily30), - daily90d: this.toChartData(daily90), - monthly12m: this.toChartData(monthly12), - }; - - this.setCache(cacheKey, result, this.TIMESERIES_TTL); - return result; - } - - async getUserTimeSeries(): Promise { - const cacheKey = 'analytics_user_ts'; - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - const weeks = await this.userRepository - .createQueryBuilder('user') - .select("strftime('%Y-%W', user.updatedAt)", 'week') - .addSelect('COUNT(DISTINCT user.id)', 'count') - .where('user.updatedAt > :since', { since: this.daysAgo(84) }) - .groupBy('week') - .orderBy('week', 'ASC') - .getRawMany<{ week: string; count: string }>(); - - const result: UserTimeSeries = { - weeklyActive12w: { - labels: weeks.map((w) => w.week), - values: weeks.map((w) => parseInt(w.count)), - }, - }; - - this.setCache(cacheKey, result, this.TIMESERIES_TTL); - return result; - } - - async getVolumeStats( - period: 'daily' | 'weekly' | 'monthly', - from?: string, - to?: string, - ): Promise { - const cacheKey = `analytics_volume_${period}_${from}_${to}`; - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - const formatMap: Record = { - daily: '%Y-%m-%d', - weekly: '%Y-%W', - monthly: '%Y-%m', - }; - const dateFormat = formatMap[period] ?? '%Y-%m-%d'; - - const query = this.escrowRepository - .createQueryBuilder('escrow') - .select(`strftime('${dateFormat}', escrow.createdAt)`, 'bucket') - .addSelect('COUNT(*)', 'count') - .addSelect('SUM(escrow.amount)', 'volume') - .groupBy('bucket') - .orderBy('bucket', 'ASC'); - - if (from && to) { - query.where('escrow.createdAt BETWEEN :from AND :to', { from, to }); - } - - const results = await query.getRawMany<{ - bucket: string; - count: string; - volume: string | null; - }>(); - - const stats = results.map((r) => ({ - period: r.bucket, - count: parseInt(r.count), - volume: parseFloat(r.volume || '0'), - })); - - this.setCache(cacheKey, stats, this.TIMESERIES_TTL); - return stats; - } - - async getTopUsers(limit: number = 10): Promise { - const cacheKey = `analytics_top_users_${limit}`; - const cached = this.getFromCache(cacheKey); - if (cached) return cached; - - const topUsers = await this.userRepository - .createQueryBuilder('user') - .leftJoin('escrow_parties', 'party', 'party.userId = user.id') - .leftJoin('escrows', 'escrow', 'escrow.id = party.escrowId') - .select('user.walletAddress', 'walletAddress') - .addSelect('COUNT(DISTINCT escrow.id)', 'escrowCount') - .addSelect('SUM(escrow.amount)', 'totalVolume') - .addSelect( - 'SUM(CASE WHEN escrow.status = :completed THEN 1 ELSE 0 END) * 1.0 / NULLIF(COUNT(escrow.id), 0)', - 'completionRate', - ) - .setParameter('completed', EscrowStatus.COMPLETED) - .groupBy('user.id') - .orderBy('totalVolume', 'DESC') - .limit(limit) - .getRawMany<{ - walletAddress: string; - escrowCount: string; - totalVolume: string | null; - completionRate: string | null; - }>(); - - const stats = topUsers.map((u) => ({ - walletAddress: u.walletAddress, - escrowCount: parseInt(u.escrowCount), - totalVolume: parseFloat(u.totalVolume || '0'), - completionRate: parseFloat(parseFloat(u.completionRate || '0').toFixed(4)), - })); - - this.setCache(cacheKey, stats, this.SUMMARY_TTL); - return stats; - } - - // Keep legacy method for backwards compat - async getOverview() { - return this.getSummary(); - } - - private async queryVolumeSeries( - fmt: string, - since: Date, - from?: string, - to?: string, - ) { - const query = this.escrowRepository - .createQueryBuilder('escrow') - .select(`strftime('${fmt}', escrow.createdAt)`, 'bucket') - .addSelect('SUM(escrow.amount)', 'volume') - .groupBy('bucket') - .orderBy('bucket', 'ASC'); - - if (from && to) { - query.where('escrow.createdAt BETWEEN :from AND :to', { from, to }); - } else { - query.where('escrow.createdAt >= :since', { since: since.toISOString() }); - } - - return query.getRawMany<{ bucket: string; volume: string | null }>(); - } - - private toChartData(rows: { bucket: string; volume: string | null }[]): ChartData { - return { - labels: rows.map((r) => r.bucket), - values: rows.map((r) => parseFloat(r.volume || '0')), - }; - } -} diff --git a/apps/backend/src/modules/admin/services/consistency-checker.service.ts b/apps/backend/src/modules/admin/services/consistency-checker.service.ts deleted file mode 100644 index 37018639..00000000 --- a/apps/backend/src/modules/admin/services/consistency-checker.service.ts +++ /dev/null @@ -1,187 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { - ConsistencyCheckRequest, - ConsistencyCheckResponse, - EscrowDiffReport, - FieldMismatch, -} from '../dto/consistency-check.dto'; -import { EscrowService } from '../../escrow/services/escrow.service'; -import { - SorobanClientService, - OnchainEscrow, -} from '../../../services/stellar/soroban-client.service'; -import { Escrow } from '../../escrow/entities/escrow.entity'; - -@Injectable() -export class ConsistencyCheckerService { - private readonly logger = new Logger(ConsistencyCheckerService.name); - - constructor( - private readonly escrowService: EscrowService, - private readonly sorobanClient: SorobanClientService, - ) {} - - async checkConsistency( - request: ConsistencyCheckRequest, - ): Promise { - // ... (rest of the method logic remains similar, but using this.sorobanClient.getEscrow) - // I'll replace the loop part below - // 1. Resolve escrow IDs - let escrowIds: string[] = []; - if ('escrowIds' in request) { - escrowIds = request.escrowIds.map(String); - } else if ('fromId' in request && 'toId' in request) { - const from = Number(request.fromId); - const to = Number(request.toId); - if (isNaN(from) || isNaN(to) || from > to) { - throw new Error('Invalid fromId/toId'); - } - escrowIds = Array.from({ length: to - from + 1 }, (_, i) => - String(from + i), - ); - } - // Limit batch size - const MAX = 50; - if (escrowIds.length > MAX) { - throw new Error(`Max ${MAX} escrows per request`); - } - - const reports: EscrowDiffReport[] = []; - let totalInconsistent = 0, - totalMissingInDb = 0, - totalMissingOnChain = 0, - totalErrored = 0; - - for (const escrowId of escrowIds) { - try { - // Fetch from DB - let dbEscrow: unknown = null; - try { - dbEscrow = await this.escrowService.findOne(escrowId); - } catch (error) { - this.logger.warn( - `Escrow ${escrowId} not found in DB: ${(error as Error).message}`, - ); - dbEscrow = null; - } - // Fetch from on-chain (Soroban) - let onchainEscrow: unknown = null; - try { - onchainEscrow = await this.sorobanClient.getEscrow(Number(escrowId)); - } catch (error) { - this.logger.warn( - `Escrow ${escrowId} not found on-chain: ${(error as Error).message}`, - ); - onchainEscrow = null; - } - - if (!dbEscrow && !onchainEscrow) { - reports.push({ - escrowId: Number(escrowId), - isConsistent: false, - fieldsMismatched: [], - missingInDb: true, - missingOnChain: true, - }); - totalMissingInDb++; - totalMissingOnChain++; - continue; - } - if (!dbEscrow) { - reports.push({ - escrowId: Number(escrowId), - isConsistent: false, - fieldsMismatched: [], - missingInDb: true, - }); - totalMissingInDb++; - continue; - } - if (!onchainEscrow) { - reports.push({ - escrowId: Number(escrowId), - isConsistent: false, - fieldsMismatched: [], - missingOnChain: true, - }); - totalMissingOnChain++; - continue; - } - - // Compare fields - const mismatches = this.compareEscrow( - dbEscrow as Escrow, - onchainEscrow as OnchainEscrow, - ); - const isConsistent = mismatches.length === 0; - if (!isConsistent) totalInconsistent++; - reports.push({ - escrowId: Number(escrowId), - isConsistent, - fieldsMismatched: mismatches, - }); - } catch (err) { - this.logger.error(`Error checking escrow ${escrowId}: ${err}`); - reports.push({ - escrowId: Number(escrowId), - isConsistent: false, - fieldsMismatched: [], - error: String(err), - }); - totalErrored++; - } - } - - return { - reports, - summary: { - totalChecked: escrowIds.length, - totalInconsistent, - totalMissingInDb, - totalMissingOnChain, - totalErrored, - }, - }; - } - - // Helper: compare two escrow objects and return diff - compareEscrow( - dbEscrow: Escrow, - onchainEscrow: OnchainEscrow, - ): FieldMismatch[] { - const mismatches: FieldMismatch[] = []; - - // Compare Status (with mapping) - const mappedOnchainStatus = this.mapContractStatus(onchainEscrow.status); - if (mappedOnchainStatus !== (dbEscrow.status as string)) { - mismatches.push({ - fieldName: 'status', - dbValue: dbEscrow.status, - onchainValue: onchainEscrow.status, - }); - } - - // Compare Amount - if (Number(onchainEscrow.amount) !== Number(dbEscrow.amount)) { - mismatches.push({ - fieldName: 'amount', - dbValue: dbEscrow.amount, - onchainValue: onchainEscrow.amount, - }); - } - - return mismatches; - } - - private mapContractStatus(contractStatus: string): string { - const statusMap: Record = { - Created: 'pending', - Active: 'active', - Completed: 'completed', - Cancelled: 'cancelled', - Disputed: 'disputed', - ArbiterResolved: 'completed', - }; - return statusMap[contractStatus] || contractStatus.toLowerCase(); - } -} diff --git a/apps/backend/src/modules/assets/admin-assets.controller.ts b/apps/backend/src/modules/assets/admin-assets.controller.ts deleted file mode 100644 index e79f790f..00000000 --- a/apps/backend/src/modules/assets/admin-assets.controller.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - Controller, - Get, - Post, - Body, - Patch, - Param, - Delete, - UseGuards, -} from '@nestjs/common'; -import { AssetsService } from './assets.service'; -import { CreateAssetDto, UpdateAssetDto } from './dto/asset.dto'; -import { AuthGuard } from '../auth/middleware/auth.guard'; -import { AdminGuard } from '../auth/middleware/admin.guard'; - -@Controller('admin/assets') -@UseGuards(AuthGuard, AdminGuard) -export class AdminAssetsController { - constructor(private readonly assetsService: AssetsService) {} - - @Post() - create(@Body() createAssetDto: CreateAssetDto) { - return this.assetsService.create(createAssetDto); - } - - @Get() - findAll() { - return this.assetsService.findAll(false); - } - - @Get(':id') - findOne(@Param('id') id: string) { - return this.assetsService.findOne(id); - } - - @Patch(':id') - update(@Param('id') id: string, @Body() updateAssetDto: UpdateAssetDto) { - return this.assetsService.update(id, updateAssetDto); - } - - @Delete(':id') - remove(@Param('id') id: string) { - return this.assetsService.remove(id); - } -} diff --git a/apps/backend/src/modules/assets/assets.controller.ts b/apps/backend/src/modules/assets/assets.controller.ts deleted file mode 100644 index 541f26d5..00000000 --- a/apps/backend/src/modules/assets/assets.controller.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { - Controller, - Get, - Query, - UseGuards, - Request, - BadRequestException, -} from '@nestjs/common'; -import { Request as ExpressRequest } from 'express'; -import { AssetsService } from './assets.service'; -import { AuthGuard } from '../auth/middleware/auth.guard'; -import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; - -interface AuthenticatedRequest extends ExpressRequest { - user: { - userId: string; - walletAddress: string; - email: string; - role: string; - }; -} - -@Controller('assets') -@ApiTags('assets') -export class AssetsController { - constructor(private readonly assetsService: AssetsService) {} - - @Get() - async findAllActive() { - return this.assetsService.findAll(true); - } - - @Get('balance') - @UseGuards(AuthGuard) - @ApiBearerAuth() - async getBalance( - @Query('assetCode') assetCode: string, - @Query('issuer') issuer: string | undefined, - @Request() req: AuthenticatedRequest, - ) { - if (!assetCode) { - throw new BadRequestException('assetCode query parameter is required'); - } - const walletAddress = req.user.walletAddress; - return this.assetsService.getBalance(walletAddress, assetCode, issuer); - } -} diff --git a/apps/backend/src/modules/assets/assets.module.ts b/apps/backend/src/modules/assets/assets.module.ts deleted file mode 100644 index 81198868..00000000 --- a/apps/backend/src/modules/assets/assets.module.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { AssetsService } from './assets.service'; -import { AssetsController } from './assets.controller'; -import { AdminAssetsController } from './admin-assets.controller'; -import { AllowedAsset } from './entities/allowed-asset.entity'; -import { StellarModule } from '../stellar/stellar.module'; -import { AuthModule } from '../auth/auth.module'; -import { UserModule } from '../user/user.module'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([AllowedAsset]), - StellarModule, - AuthModule, - UserModule, - ], - controllers: [AssetsController, AdminAssetsController], - providers: [AssetsService], - exports: [AssetsService], -}) -export class AssetsModule {} diff --git a/apps/backend/src/modules/assets/assets.service.ts b/apps/backend/src/modules/assets/assets.service.ts deleted file mode 100644 index f11e2f40..00000000 --- a/apps/backend/src/modules/assets/assets.service.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { - Injectable, - NotFoundException, - BadRequestException, -} from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { AllowedAsset } from './entities/allowed-asset.entity'; -import { CreateAssetDto, UpdateAssetDto } from './dto/asset.dto'; -import { StellarService } from '../../services/stellar.service'; - -@Injectable() -export class AssetsService { - constructor( - @InjectRepository(AllowedAsset) - private readonly assetRepository: Repository, - private readonly stellarService: StellarService, - ) {} - - async create(createAssetDto: CreateAssetDto): Promise { - if (createAssetDto.code !== 'XLM') { - if (!createAssetDto.issuer) { - throw new BadRequestException( - 'Issuer is required for non-native assets', - ); - } - - const isValid = await this.stellarService.validateAsset( - createAssetDto.code, - createAssetDto.issuer, - ); - if (!isValid) { - throw new BadRequestException( - `Asset ${createAssetDto.code} from ${createAssetDto.issuer} does not exist on Stellar`, - ); - } - } - - const asset = this.assetRepository.create(createAssetDto); - return this.assetRepository.save(asset); - } - - async findAll(activeOnly = false): Promise { - const where = activeOnly ? { active: true } : {}; - return this.assetRepository.find({ where }); - } - - async findOne(id: string): Promise { - const asset = await this.assetRepository.findOne({ where: { id } }); - if (!asset) { - throw new NotFoundException(`Asset with ID ${id} not found`); - } - return asset; - } - - async update( - id: string, - updateAssetDto: UpdateAssetDto, - ): Promise { - const asset = await this.findOne(id); - - if ( - updateAssetDto.code && - updateAssetDto.code !== 'XLM' && - updateAssetDto.issuer - ) { - const isValid = await this.stellarService.validateAsset( - updateAssetDto.code, - updateAssetDto.issuer, - ); - if (!isValid) { - throw new BadRequestException( - `Asset ${updateAssetDto.code} from ${updateAssetDto.issuer} does not exist on Stellar`, - ); - } - } - - Object.assign(asset, updateAssetDto); - return this.assetRepository.save(asset); - } - - async remove(id: string): Promise { - const asset = await this.findOne(id); - await this.assetRepository.remove(asset); - } - - async getBalance( - walletAddress: string, - assetCode: string, - issuer?: string, - ): Promise<{ balance: number; assetCode: string; issuer?: string }> { - try { - const account = await this.stellarService.getAccount(walletAddress); - const balanceItem = account.balances.find((b) => { - if (assetCode === 'XLM' || assetCode === 'native') { - return b.asset_type === 'native'; - } else { - return b.asset_code === assetCode && b.asset_issuer === issuer; - } - }); - - if (!balanceItem) { - if (assetCode === 'XLM') { - throw new BadRequestException( - 'Account has no native XLM balance or is not funded', - ); - } else { - throw new BadRequestException( - `Account does not trust the asset ${assetCode}. Please establish a trustline first.`, - ); - } - } - - return { - balance: parseFloat(balanceItem.balance), - assetCode, - issuer, - }; - } catch (error) { - if (error instanceof BadRequestException) { - throw error; - } - throw new BadRequestException( - `Failed to fetch balance: account ${walletAddress} may not exist or cannot be reached.`, - ); - } - } -} diff --git a/apps/backend/src/modules/assets/dto/asset.dto.ts b/apps/backend/src/modules/assets/dto/asset.dto.ts deleted file mode 100644 index e8e481e9..00000000 --- a/apps/backend/src/modules/assets/dto/asset.dto.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { - IsString, - IsNotEmpty, - IsOptional, - IsInt, - Min, - Max, - IsBoolean, - Length, -} from 'class-validator'; - -export class CreateAssetDto { - @IsString() - @IsNotEmpty() - @Length(1, 12) - code: string; - - @IsString() - @IsOptional() - @Length(56, 56) - issuer?: string; - - @IsString() - @IsNotEmpty() - displayName: string; - - @IsString() - @IsOptional() - iconUrl?: string; - - @IsInt() - @Min(0) - @Max(18) - @IsOptional() - decimals?: number; - - @IsBoolean() - @IsOptional() - active?: boolean; -} - -export class UpdateAssetDto { - @IsString() - @IsOptional() - @Length(1, 12) - code?: string; - - @IsString() - @IsOptional() - @Length(56, 56) - issuer?: string; - - @IsString() - @IsOptional() - displayName?: string; - - @IsString() - @IsOptional() - iconUrl?: string; - - @IsInt() - @Min(0) - @Max(18) - @IsOptional() - decimals?: number; - - @IsBoolean() - @IsOptional() - active?: boolean; -} diff --git a/apps/backend/src/modules/assets/entities/allowed-asset.entity.ts b/apps/backend/src/modules/assets/entities/allowed-asset.entity.ts deleted file mode 100644 index 292afb89..00000000 --- a/apps/backend/src/modules/assets/entities/allowed-asset.entity.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - Entity, - Column, - PrimaryGeneratedColumn, - CreateDateColumn, - UpdateDateColumn, -} from 'typeorm'; - -@Entity('allowed_assets') -export class AllowedAsset { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column({ type: 'varchar', length: 12 }) - code: string; - - @Column({ type: 'varchar', length: 56, nullable: true }) // null for native XLM - issuer: string; - - @Column({ type: 'varchar' }) - displayName: string; - - @Column({ type: 'varchar', nullable: true }) - iconUrl: string; - - @Column({ type: 'int', default: 7 }) - decimals: number; - - @Column({ type: 'boolean', default: true }) - active: boolean; - - @CreateDateColumn() - createdAt: Date; - - @UpdateDateColumn() - updatedAt: Date; -} diff --git a/apps/backend/src/modules/auth/auth.module.ts b/apps/backend/src/modules/auth/auth.module.ts deleted file mode 100644 index 347a347d..00000000 --- a/apps/backend/src/modules/auth/auth.module.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Module } from '@nestjs/common'; -import { JwtModule } from '@nestjs/jwt'; -import { ThrottlerModule } from '@nestjs/throttler'; -import { AuthController } from './controllers/auth.controller'; -import { AuthService } from './services/auth.service'; -import { AuthGuard } from './middleware/auth.guard'; -import { AdminGuard } from './middleware/admin.guard'; -import { UserModule } from '../user/user.module'; -import { IpfsModule } from '../ipfs/ipfs.module'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { EmailVerification } from '../user/entities/email-verification.entity'; - -@Module({ - imports: [ - UserModule, - IpfsModule, - TypeOrmModule.forFeature([EmailVerification]), - JwtModule.registerAsync({ - useFactory: () => ({ - secret: - process.env.JWT_SECRET || 'your-secret-key-change-in-production', - }), - }), - ThrottlerModule.forRoot([ - { - ttl: 60000, - limit: process.env.NODE_ENV === 'test' ? 1000 : 10, - }, - ]), - ], - controllers: [AuthController], - providers: [AuthService, AuthGuard, AdminGuard], - exports: [AuthService, AuthGuard, AdminGuard], -}) -export class AuthModule {} diff --git a/apps/backend/src/modules/auth/controllers/auth.controller.ts b/apps/backend/src/modules/auth/controllers/auth.controller.ts deleted file mode 100644 index ae5c9c08..00000000 --- a/apps/backend/src/modules/auth/controllers/auth.controller.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { - Controller, - Post, - Get, - Body, - UseGuards, - Req, - HttpCode, - HttpStatus, - Patch, - Query, - UseInterceptors, - UploadedFile, -} from '@nestjs/common'; -import { FileInterceptor } from '@nestjs/platform-express'; -import { Request } from 'express'; -import { Throttle } from '@nestjs/throttler'; -import { AuthService } from '../services/auth.service'; -import { - ChallengeDto, - VerifyDto, - RefreshTokenDto, - LogoutDto, -} from '../dto/auth.dto'; -import { UpdateProfileDto } from '../dto/profile.dto'; -import { AuthGuard } from '../middleware/auth.guard'; -import { AuthThrottlerGuard } from '../middleware/auth-throttler.guard'; - -@Controller('auth') -@UseGuards(AuthThrottlerGuard) -export class AuthController { - constructor(private readonly authService: AuthService) {} - - @Post('challenge') - @HttpCode(HttpStatus.OK) - @Throttle({ default: { limit: 10, ttl: 60000 } }) - async challenge(@Body() challengeDto: ChallengeDto) { - return this.authService.generateChallenge(challengeDto.walletAddress); - } - - @Post('verify') - @HttpCode(HttpStatus.OK) - @Throttle({ default: { limit: 5, ttl: 60000 } }) - async verify(@Body() verifyDto: VerifyDto) { - return this.authService.verifySignature( - verifyDto.signature, - verifyDto.publicKey, - ); - } - - @Post('refresh') - @HttpCode(HttpStatus.OK) - @Throttle({ default: { limit: 20, ttl: 60000 } }) - async refresh(@Body() refreshTokenDto: RefreshTokenDto) { - return this.authService.refreshAccessToken(refreshTokenDto.refreshToken); - } - - @Get('me') - @UseGuards(AuthGuard) - async getCurrentUser(@Req() req: Request & { user: { userId: string } }) { - const user = await this.authService.getCurrentUser(req.user.userId); - return { - id: user.id, - walletAddress: user.walletAddress, - isActive: user.isActive, - createdAt: user.createdAt, - displayName: user.displayName, - email: user.email, - emailVerified: user.emailVerified, - avatarUrl: user.avatarUrl, - bio: user.bio, - preferredAsset: user.preferredAsset, - }; - } - - @Patch('profile') - @UseGuards(AuthGuard) - async updateProfile( - @Req() req: Request & { user: { userId: string } }, - @Body() updateProfileDto: UpdateProfileDto, - ) { - const user = await this.authService.updateProfile( - req.user.userId, - updateProfileDto, - ); - return { - id: user.id, - walletAddress: user.walletAddress, - displayName: user.displayName, - email: user.email, - emailVerified: user.emailVerified, - avatarUrl: user.avatarUrl, - bio: user.bio, - preferredAsset: user.preferredAsset, - }; - } - - @Post('profile/avatar') - @UseGuards(AuthGuard) - @UseInterceptors(FileInterceptor('avatar')) - async uploadAvatar( - @Req() req: Request & { user: { userId: string } }, - @UploadedFile() file: { buffer: Buffer; originalname: string }, - ) { - const user = await this.authService.uploadAvatar(req.user.userId, file); - return { - id: user.id, - avatarUrl: user.avatarUrl, - }; - } - - @Post('profile/verify-email') - @UseGuards(AuthGuard) - @HttpCode(HttpStatus.OK) - async sendEmailVerification( - @Req() req: Request & { user: { userId: string } }, - ) { - await this.authService.sendEmailVerification(req.user.userId); - return { message: 'Verification email sent' }; - } - - @Get('profile/verify-email') - async verifyEmail(@Query('token') token: string) { - await this.authService.verifyEmail(token); - return { message: 'Email verified successfully' }; - } - - @Post('logout') - @UseGuards(AuthGuard) - @HttpCode(HttpStatus.OK) - @Throttle({ default: { limit: 10, ttl: 60000 } }) - async logout(@Body() logoutDto: LogoutDto) { - await this.authService.logout(logoutDto.refreshToken); - return { message: 'Successfully logged out' }; - } -} diff --git a/apps/backend/src/modules/auth/dto/auth.dto.ts b/apps/backend/src/modules/auth/dto/auth.dto.ts deleted file mode 100644 index 907951db..00000000 --- a/apps/backend/src/modules/auth/dto/auth.dto.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { IsString, IsNotEmpty, Length, Matches } from 'class-validator'; - -export class ChallengeDto { - @IsString() - @IsNotEmpty() - @Length(1, 56) - @Matches(/^G[A-Z0-9]{55}$/) - walletAddress: string; -} - -export class VerifyDto { - @IsString() - @IsNotEmpty() - signature: string; - - @IsString() - @IsNotEmpty() - @Length(1, 56) - @Matches(/^G[A-Z0-9]{55}$/) - publicKey: string; -} - -export class RefreshTokenDto { - @IsString() - @IsNotEmpty() - refreshToken: string; -} - -export class LogoutDto { - @IsString() - @IsNotEmpty() - refreshToken: string; -} diff --git a/apps/backend/src/modules/auth/dto/profile.dto.ts b/apps/backend/src/modules/auth/dto/profile.dto.ts deleted file mode 100644 index 031e5c64..00000000 --- a/apps/backend/src/modules/auth/dto/profile.dto.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - IsBoolean, - IsEmail, - IsOptional, - IsString, - MaxLength, -} from 'class-validator'; - -export class UpdateProfileDto { - @IsOptional() - @IsString() - @MaxLength(100) - displayName?: string; - - @IsOptional() - @IsEmail() - @MaxLength(255) - email?: string; - - @IsOptional() - @IsBoolean() - emailVerified?: boolean; - - @IsOptional() - @IsString() - bio?: string; - - @IsOptional() - @IsString() - @MaxLength(20) - preferredAsset?: string; -} diff --git a/apps/backend/src/modules/auth/middleware/admin.guard.ts b/apps/backend/src/modules/auth/middleware/admin.guard.ts deleted file mode 100644 index 79c14cdc..00000000 --- a/apps/backend/src/modules/auth/middleware/admin.guard.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { - Injectable, - CanActivate, - ExecutionContext, - ForbiddenException, -} from '@nestjs/common'; -import { UserRole } from '../../user/entities/user-role.enum'; - -@Injectable() -export class AdminGuard implements CanActivate { - canActivate(context: ExecutionContext): boolean { - const request = context - .switchToHttp() - .getRequest<{ user?: { role: UserRole } }>(); - const user = request['user']; - - if (!user) { - throw new ForbiddenException('User not authenticated'); - } - - if (user.role !== UserRole.ADMIN && user.role !== UserRole.SUPER_ADMIN) { - throw new ForbiddenException('Admin access required'); - } - - return true; - } -} diff --git a/apps/backend/src/modules/auth/middleware/auth-throttler.guard.ts b/apps/backend/src/modules/auth/middleware/auth-throttler.guard.ts deleted file mode 100644 index 69962e3a..00000000 --- a/apps/backend/src/modules/auth/middleware/auth-throttler.guard.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { ThrottlerGuard } from '@nestjs/throttler'; -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class AuthThrottlerGuard extends ThrottlerGuard { - // eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-explicit-any - protected async getTracker(req: Record): Promise { - // Get client IP, handle proxy headers - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - const forwardedFor = req.headers?.['x-forwarded-for']; - if (typeof forwardedFor === 'string') { - const firstIp = forwardedFor.split(',')[0]?.trim(); - if (firstIp) return firstIp; - } - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access - return req.ip || req.connection?.remoteAddress || 'unknown-ip'; - } -} diff --git a/apps/backend/src/modules/auth/middleware/auth.guard.ts b/apps/backend/src/modules/auth/middleware/auth.guard.ts deleted file mode 100644 index fd9ea0cf..00000000 --- a/apps/backend/src/modules/auth/middleware/auth.guard.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - Injectable, - CanActivate, - ExecutionContext, - UnauthorizedException, -} from '@nestjs/common'; -import { Request } from 'express'; -import { AuthService } from '../services/auth.service'; - -@Injectable() -export class AuthGuard implements CanActivate { - constructor(private authService: AuthService) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - const token = this.extractTokenFromHeader(request); - - if (!token) { - throw new UnauthorizedException('Access token is required'); - } - - try { - const payload = await this.authService.validateToken(token); - request['user'] = payload; - return true; - } catch { - throw new UnauthorizedException('Invalid or expired token'); - } - } - - private extractTokenFromHeader(request: Request): string | undefined { - const authHeader = request.headers.authorization; - if (authHeader && authHeader.startsWith('Bearer ')) { - return authHeader.substring(7); - } - return undefined; - } -} diff --git a/apps/backend/src/modules/auth/services/auth.service.spec.ts b/apps/backend/src/modules/auth/services/auth.service.spec.ts deleted file mode 100644 index 104ce86d..00000000 --- a/apps/backend/src/modules/auth/services/auth.service.spec.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthService } from './auth.service'; -import { UserService } from '../../user/user.service'; -import { JwtService } from '@nestjs/jwt'; -import { ConfigService } from '@nestjs/config'; -import { UnauthorizedException } from '@nestjs/common'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { EmailVerification } from '../../user/entities/email-verification.entity'; -import { IpfsService } from '../../ipfs/ipfs.service'; - -// Mock Stellar SDK -jest.mock('stellar-sdk', () => ({ - Keypair: { - fromPublicKey: jest.fn().mockReturnValue({ - verify: jest.fn().mockReturnValue(true), - }), - }, -})); - -describe('AuthService', () => { - let service: AuthService; - let userService: jest.Mocked; - let jwtService: jest.Mocked; - let configService: jest.Mocked; - let emailVerificationRepository: any; - let ipfsService: any; - - const mockUser = { - id: 'user-id', - walletAddress: 'GD...123', - nonce: 'test-nonce', - }; - - const mockRefreshToken = { - token: 'refresh-token', - user: mockUser, - expiresAt: new Date(Date.now() + 1000 * 60 * 60), - }; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - AuthService, - { - provide: UserService, - useValue: { - findByWalletAddress: jest.fn(), - create: jest.fn(), - update: jest.fn(), - findRefreshToken: jest.fn(), - invalidateRefreshToken: jest.fn(), - findById: jest.fn(), - createRefreshToken: jest.fn(), - }, - }, - { - provide: JwtService, - useValue: { - sign: jest.fn().mockReturnValue('access-token'), - verifyAsync: jest.fn(), - }, - }, - { - provide: ConfigService, - useValue: { - get: jest.fn().mockReturnValue('jwt-secret'), - }, - }, - { - provide: getRepositoryToken(EmailVerification), - useValue: { - create: jest.fn(), - save: jest.fn(), - findOne: jest.fn(), - }, - }, - { - provide: IpfsService, - useValue: { - uploadFile: jest.fn(), - getGatewayUrl: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(AuthService); - userService = module.get(UserService); - jwtService = module.get(JwtService); - configService = module.get(ConfigService); - emailVerificationRepository = module.get( - getRepositoryToken(EmailVerification), - ); - ipfsService = module.get(IpfsService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); - - describe('generateChallenge', () => { - it('should create a new user if not exists', async () => { - userService.findByWalletAddress.mockResolvedValue(null); - userService.create.mockResolvedValue(mockUser as any); - - const result = await service.generateChallenge('GD...123'); - - expect(result).toHaveProperty('nonce'); - expect(result).toHaveProperty('message'); - expect(userService.create).toHaveBeenCalledWith({ - walletAddress: 'GD...123', - nonce: expect.any(String), - }); - }); - - it('should update nonce if user exists', async () => { - userService.findByWalletAddress.mockResolvedValue(mockUser as any); - userService.update.mockResolvedValue(mockUser as any); - - const result = await service.generateChallenge('GD...123'); - - expect(result).toHaveProperty('nonce'); - expect(userService.update).toHaveBeenCalledWith(mockUser.id, { - nonce: expect.any(String), - }); - }); - }); - - describe('verifySignature', () => { - it('should throw UnauthorizedException if user not found', async () => { - userService.findByWalletAddress.mockResolvedValue(null); - - await expect(service.verifySignature('sig', 'GD...123')).rejects.toThrow( - UnauthorizedException, - ); - }); - - it('should return tokens on valid signature', async () => { - userService.findByWalletAddress.mockResolvedValue(mockUser as any); - userService.update.mockResolvedValue(mockUser as any); - userService.createRefreshToken.mockResolvedValue({} as any); - - const result = await service.verifySignature('sig', 'GD...123'); - - expect(result).toHaveProperty('accessToken'); - expect(result).toHaveProperty('refreshToken'); - expect(userService.update).toHaveBeenCalledWith(mockUser.id, { - nonce: undefined, - }); - }); - }); - - describe('refreshAccessToken', () => { - it('should throw if token invalid or expired', async () => { - userService.findRefreshToken.mockResolvedValue(null); - await expect(service.refreshAccessToken('invalid')).rejects.toThrow( - UnauthorizedException, - ); - - userService.findRefreshToken.mockResolvedValue({ - ...mockRefreshToken, - expiresAt: new Date(0), - } as any); - await expect(service.refreshAccessToken('expired')).rejects.toThrow( - UnauthorizedException, - ); - }); - - it('should return new tokens', async () => { - userService.findRefreshToken.mockResolvedValue(mockRefreshToken as any); - userService.createRefreshToken.mockResolvedValue({} as any); - - const result = await service.refreshAccessToken('refresh-token'); - - expect(result.accessToken).toBe('access-token'); - expect(userService.invalidateRefreshToken).toHaveBeenCalledWith( - 'refresh-token', - ); - }); - }); - - describe('validateToken', () => { - it('should return payload if token is valid', async () => { - jwtService.verifyAsync.mockResolvedValue({ - sub: 'user-id', - walletAddress: 'GD...123', - type: 'access', - }); - - const result = await service.validateToken('valid-token'); - - expect(result).toEqual({ - userId: 'user-id', - walletAddress: 'GD...123', - }); - }); - - it('should throw if token type is not access', async () => { - jwtService.verifyAsync.mockResolvedValue({ - sub: 'user-id', - walletAddress: 'GD...123', - type: 'refresh', - }); - - await expect(service.validateToken('invalid-type')).rejects.toThrow( - UnauthorizedException, - ); - }); - }); -}); diff --git a/apps/backend/src/modules/auth/services/auth.service.ts b/apps/backend/src/modules/auth/services/auth.service.ts deleted file mode 100644 index 313305d2..00000000 --- a/apps/backend/src/modules/auth/services/auth.service.ts +++ /dev/null @@ -1,254 +0,0 @@ -import { - Injectable, - UnauthorizedException, - BadRequestException, -} from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { JwtService } from '@nestjs/jwt'; -import * as crypto from 'crypto'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { User } from '../../user/entities/user.entity'; -import { UserService } from '../../user/user.service'; -import { EmailVerification } from '../../user/entities/email-verification.entity'; -import { UpdateProfileDto } from '../dto/profile.dto'; -import { IpfsService } from '../../ipfs/ipfs.service'; - -// Stellar SDK types for signature verification -interface StellarKeypair { - verify(data: Buffer, signature: Buffer): boolean; -} - -interface StellarSdkModule { - Keypair: { - fromPublicKey(publicKey: string): StellarKeypair; - }; -} - -// eslint-disable-next-line @typescript-eslint/no-require-imports -const StellarSdk: StellarSdkModule = require('stellar-sdk') as StellarSdkModule; - -@Injectable() -export class AuthService { - constructor( - private userService: UserService, - private jwtService: JwtService, - private configService: ConfigService, - @InjectRepository(EmailVerification) - private emailVerificationRepository: Repository, - private ipfsService: IpfsService, - ) {} - - async generateChallenge( - walletAddress: string, - ): Promise<{ nonce: string; message: string }> { - const nonce = crypto.randomBytes(16).toString('hex'); - const message = `Sign this message to authenticate with Vaultix: ${nonce}`; - - let user = await this.userService.findByWalletAddress(walletAddress); - - if (!user) { - user = await this.userService.create({ - walletAddress, - nonce, - }); - } else { - user = await this.userService.update(user.id, { nonce }); - } - - return { nonce, message }; - } - - async verifySignature( - signature: string, - publicKey: string, - ): Promise<{ accessToken: string; refreshToken: string }> { - // Derive walletAddress from publicKey (trusted source after signature verification) - const walletAddress = publicKey; - - const user = await this.userService.findByWalletAddress(walletAddress); - - if (!user || !user.nonce) { - throw new UnauthorizedException( - 'Invalid challenge. Please request a new one.', - ); - } - - const message = `Sign this message to authenticate with Vaultix: ${user.nonce}`; - - try { - const verifier = StellarSdk.Keypair.fromPublicKey(publicKey); - const signatureBuffer = Buffer.from(signature, 'hex'); - const messageBuffer = Buffer.from(message); - const isValid = verifier.verify(messageBuffer, signatureBuffer); - - if (!isValid) { - throw new UnauthorizedException('Invalid signature'); - } - } catch { - throw new UnauthorizedException('Signature verification failed'); - } - - await this.userService.update(user.id, { nonce: undefined }); - - const accessToken = this.generateAccessToken(user.id, walletAddress); - const refreshToken = await this.generateRefreshToken(user.id); - - return { accessToken, refreshToken }; - } - - async refreshAccessToken( - refreshToken: string, - ): Promise<{ accessToken: string; refreshToken: string }> { - const token = await this.userService.findRefreshToken(refreshToken); - - if (!token || token.expiresAt < new Date()) { - throw new UnauthorizedException('Invalid or expired refresh token'); - } - - await this.userService.invalidateRefreshToken(refreshToken); - - const newAccessToken = this.generateAccessToken( - token.user.id, - token.user.walletAddress, - ); - const newRefreshToken = await this.generateRefreshToken(token.user.id); - - return { accessToken: newAccessToken, refreshToken: newRefreshToken }; - } - - async logout(refreshToken: string): Promise { - await this.userService.invalidateRefreshToken(refreshToken); - } - - async getCurrentUser(userId: string): Promise { - const user = await this.userService.findById(userId); - - if (!user) { - throw new UnauthorizedException('User not found'); - } - - return user; - } - - async updateProfile( - userId: string, - updateProfileDto: UpdateProfileDto, - ): Promise { - const user = await this.userService.findById(userId); - if (!user) { - throw new UnauthorizedException('User not found'); - } - - // If email is being updated, reset emailVerified - if (updateProfileDto.email && updateProfileDto.email !== user.email) { - updateProfileDto.emailVerified = false; - } - - return this.userService.update(userId, updateProfileDto); - } - - async uploadAvatar( - userId: string, - file: { buffer: Buffer; originalname: string }, - ): Promise { - const user = await this.userService.findById(userId); - if (!user) { - throw new UnauthorizedException('User not found'); - } - - const cid = await this.ipfsService.uploadFile( - file.buffer, - file.originalname, - ); - const avatarUrl = this.ipfsService.getGatewayUrl(cid); - - return this.userService.update(userId, { avatarUrl }); - } - - async sendEmailVerification(userId: string): Promise { - const user = await this.userService.findById(userId); - if (!user) { - throw new UnauthorizedException('User not found'); - } - if (!user.email) { - throw new BadRequestException('No email set for user'); - } - - // Generate token - const token = crypto.randomUUID(); - const expiresAt = new Date(); - expiresAt.setHours(expiresAt.getHours() + 24); - - // Save token - const emailVerification = this.emailVerificationRepository.create({ - userId, - token, - expiresAt, - }); - await this.emailVerificationRepository.save(emailVerification); - - // TODO: Actually send email (for now, just log it - console.log(`Email verification token for ${user.email}: ${token}`); - } - - async verifyEmail(token: string): Promise { - const verification = await this.emailVerificationRepository.findOne({ - where: { token, isUsed: false }, - }); - - if (!verification || verification.expiresAt < new Date()) { - throw new BadRequestException('Invalid or expired verification token'); - } - - verification.isUsed = true; - await this.emailVerificationRepository.save(verification); - - await this.userService.update(verification.userId, { emailVerified: true }); - } - - async validateToken( - token: string, - ): Promise<{ userId: string; walletAddress: string }> { - try { - const payload = (await this.jwtService.verifyAsync(token, { - secret: this.configService.get('JWT_SECRET'), - })) as unknown as { sub: string; walletAddress: string; type: string }; - - if (payload.type !== 'access') { - throw new UnauthorizedException('Invalid token type'); - } - - return { - userId: payload.sub, - walletAddress: payload.walletAddress, - }; - } catch { - throw new UnauthorizedException('Invalid token'); - } - } - - private generateAccessToken(userId: string, walletAddress: string): string { - const payload = { - sub: userId, - walletAddress, - type: 'access', - }; - - return this.jwtService.sign(payload); - } - - private async generateRefreshToken(userId: string): Promise { - const token = crypto.randomBytes(32).toString('hex'); - const expiresAt = new Date(); - expiresAt.setDate(expiresAt.getDate() + 7); // 7 days - - await this.userService.createRefreshToken({ - token, - userId, - expiresAt, - }); - - return token; - } -} diff --git a/apps/backend/src/modules/escrow/controllers/escrow-scheduler.controller.ts b/apps/backend/src/modules/escrow/controllers/escrow-scheduler.controller.ts deleted file mode 100644 index 56f5b6ea..00000000 --- a/apps/backend/src/modules/escrow/controllers/escrow-scheduler.controller.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - Controller, - Post, - Param, - UseGuards, - HttpStatus, - HttpCode, -} from '@nestjs/common'; -import { AuthGuard } from '../../auth/middleware/auth.guard'; -import { AdminGuard } from '../../auth/middleware/admin.guard'; -import { EscrowSchedulerService } from '../services/escrow-scheduler.service'; - -@Controller('escrows/scheduler') -@UseGuards(AuthGuard, AdminGuard) -export class EscrowSchedulerController { - constructor(private readonly schedulerService: EscrowSchedulerService) {} - - @Post('process-expired') - @HttpCode(HttpStatus.OK) - async processExpiredEscrows() { - await this.schedulerService.handleExpiredEscrows(); - return { message: 'Expired escrow processing initiated' }; - } - - @Post('send-warnings') - @HttpCode(HttpStatus.OK) - async sendExpirationWarnings() { - await this.schedulerService.sendExpirationWarnings(); - return { message: 'Expiration warning sending initiated' }; - } - - @Post('process/:escrowId') - @HttpCode(HttpStatus.OK) - async processEscrowManually(@Param('escrowId') escrowId: string) { - await this.schedulerService.processEscrowManually(escrowId); - return { message: `Escrow ${escrowId} processed manually` }; - } -} diff --git a/apps/backend/src/modules/escrow/controllers/escrow.controller.ts b/apps/backend/src/modules/escrow/controllers/escrow.controller.ts deleted file mode 100644 index 1b07edcd..00000000 --- a/apps/backend/src/modules/escrow/controllers/escrow.controller.ts +++ /dev/null @@ -1,478 +0,0 @@ -import { - Controller, - Get, - Post, - Patch, - Body, - Param, - Query, - UseGuards, - Request, - Req, - ForbiddenException, - UseInterceptors, - UploadedFiles, - ParseFilePipe, - MaxFileSizeValidator, - FileTypeValidator, - Res, -} from '@nestjs/common'; -import { FilesInterceptor } from '@nestjs/platform-express'; -import { ThrottlerGuard } from '@nestjs/throttler'; -import { Response } from 'express'; -import { Request as ExpressRequest } from 'express'; -import { - ApiBearerAuth, - ApiOkResponse, - ApiOperation, - ApiTags, -} from '@nestjs/swagger'; -import { AuthGuard } from '../../auth/middleware/auth.guard'; -import { AdminGuard } from '../../auth/middleware/admin.guard'; -import { EscrowAccessGuard } from '../guards/escrow-access.guard'; -import { EscrowExpireGuard } from '../guards/escrow-expire.guard'; -import { EscrowService } from '../services/escrow.service'; -import { EscrowEvidenceService } from '../services/escrow-evidence.service'; -import { IpfsService } from '../../ipfs/ipfs.service'; -import { CreateEscrowDto } from '../dto/create-escrow.dto'; -import { UpdateEscrowDto } from '../dto/update-escrow.dto'; -import { ListEscrowsDto } from '../dto/list-escrows.dto'; -import { ListEventsDto } from '../dto/list-events.dto'; -import { CancelEscrowDto } from '../dto/cancel-escrow.dto'; -import { - EscrowOverviewQueryDto, - EscrowOverviewResponseDto, -} from '../dto/escrow-overview.dto'; -import { FulfillConditionDto } from '../dto/fulfill-condition.dto'; -import { FileDisputeDto, ResolveDisputeDto } from '../dto/dispute.dto'; -import { FundEscrowDto } from '../dto/fund-escrow.dto'; -import { ExpireEscrowDto } from '../dto/expire-escrow.dto'; -import { ProposeMilestoneChangeDto } from '../dto/milestone-change.dto'; -import { - EvidenceFileMetadataDto, - UploadEvidenceResponseDto, -} from '../dto/upload-evidence.dto'; - -interface AuthenticatedRequest extends ExpressRequest { - user: { sub?: string; userId?: string; walletAddress: string }; -} - -@Controller('escrows') -@ApiTags('escrows') -@ApiBearerAuth() -@UseGuards(ThrottlerGuard, AuthGuard) -export class EscrowController { - constructor( - private readonly escrowService: EscrowService, - private readonly evidenceService: EscrowEvidenceService, - private readonly ipfsService: IpfsService, - ) {} - - private getAuthenticatedUserId(req: AuthenticatedRequest): string { - const userId = req.user.sub ?? req.user.userId; - if (!userId) { - throw new ForbiddenException('User not authenticated'); - } - - return userId; - } - - @Post() - async create( - @Body() dto: CreateEscrowDto, - @Request() req: AuthenticatedRequest, - ) { - const userId = this.getAuthenticatedUserId(req); - const ipAddress = req.ip || req.socket?.remoteAddress; - return this.escrowService.create(dto, userId, ipAddress); - } - - @Get() - async findAll( - @Query() query: ListEscrowsDto, - @Request() req: AuthenticatedRequest, - ) { - const userId = this.getAuthenticatedUserId(req); - return this.escrowService.findAll(userId, query); - } - - @Get('overview') - @ApiOperation({ - summary: 'Get paginated escrow overview for authenticated user dashboard', - }) - @ApiOkResponse({ type: EscrowOverviewResponseDto }) - async findOverview( - @Query() query: EscrowOverviewQueryDto, - @Request() req: AuthenticatedRequest, - ) { - const userId = this.getAuthenticatedUserId(req); - return this.escrowService.findOverview(userId, query); - } - - @Get('pending-invitations') - @ApiOperation({ - summary: - 'List escrows where the authenticated user has a pending party invitation', - }) - async getPendingInvitations(@Request() req: AuthenticatedRequest) { - return this.escrowService.getPendingInvitations( - this.getAuthenticatedUserId(req), - ); - } - - @Get(':id') - @UseGuards(EscrowAccessGuard) - async findOne(@Param('id') id: string) { - return this.escrowService.findOne(id); - } - - @Patch(':id') - @UseGuards(EscrowAccessGuard) - async update( - @Param('id') id: string, - @Body() dto: UpdateEscrowDto, - @Request() req: AuthenticatedRequest, - ) { - const userId = this.getAuthenticatedUserId(req); - const ipAddress = req.ip || req.socket?.remoteAddress; - return this.escrowService.update(id, dto, userId, ipAddress); - } - - @Post(':id/cancel') - @UseGuards(EscrowAccessGuard) - async cancel( - @Param('id') id: string, - @Body() dto: CancelEscrowDto, - @Request() req: AuthenticatedRequest, - ) { - const userId = this.getAuthenticatedUserId(req); - const ipAddress = req.ip || req.socket?.remoteAddress; - return this.escrowService.cancel(id, dto, userId, ipAddress); - } - - @Post(':id/expire') - @UseGuards(EscrowExpireGuard) - async expire( - @Param('id') id: string, - @Body() dto: ExpireEscrowDto, - @Request() req: AuthenticatedRequest, - ) { - const userId = this.getAuthenticatedUserId(req); - const ipAddress = req.ip || req.socket?.remoteAddress; - - return this.escrowService.expire(id, dto, userId, ipAddress); - } - - @Get(':id/events') - @UseGuards(EscrowAccessGuard) - async findEscrowEvents( - @Param('id') id: string, - @Query() query: ListEventsDto, - @Request() req: AuthenticatedRequest, - ) { - const userId = this.getAuthenticatedUserId(req); - return this.escrowService.findEvents(userId, query, id); - } - - @Post(':id/fund') - @UseGuards(EscrowAccessGuard) - async fund( - @Param('id') id: string, - @Body() dto: FundEscrowDto, - @Request() req: AuthenticatedRequest, - ) { - const ipAddress = req.ip || req.socket?.remoteAddress; - return this.escrowService.fund( - id, - dto, - this.getAuthenticatedUserId(req), - req.user.walletAddress, - ipAddress, - ); - } - - @Post(':id/release') - @UseGuards(AuthGuard) - async releaseEscrow( - @Param('id') id: string, - @Req() req: AuthenticatedRequest, - ) { - const escrow = await this.escrowService.releaseEscrow( - id, - this.getAuthenticatedUserId(req), - true, // manual trigger - ); - - return { - id: escrow.id, - status: escrow.status, - transactionHash: escrow.releaseTransactionHash, - }; - } - - @Post(':id/conditions/:conditionId/fulfill') - @UseGuards(EscrowAccessGuard) - async fulfillCondition( - @Param('id') escrowId: string, - @Param('conditionId') conditionId: string, - @Body() dto: FulfillConditionDto, - @Request() req: AuthenticatedRequest, - ) { - const userId = this.getAuthenticatedUserId(req); - const ipAddress = req.ip || req.socket?.remoteAddress; - return this.escrowService.fulfillCondition( - escrowId, - conditionId, - dto, - userId, - ipAddress, - ); - } - - @Post(':id/conditions/:conditionId/confirm') - @UseGuards(EscrowAccessGuard) - async confirmCondition( - @Param('id') escrowId: string, - @Param('conditionId') conditionId: string, - @Request() req: AuthenticatedRequest, - ) { - const userId = this.getAuthenticatedUserId(req); - const ipAddress = req.ip || req.socket?.remoteAddress; - return this.escrowService.confirmCondition( - escrowId, - conditionId, - userId, - ipAddress, - ); - } - - @Post(':id/conditions/:conditionId/propose') - @UseGuards(EscrowAccessGuard) - @ApiOperation({ summary: 'Propose a change to a pending milestone' }) - async proposeMilestoneChange( - @Param('id') escrowId: string, - @Param('conditionId') conditionId: string, - @Body() dto: ProposeMilestoneChangeDto, - @Request() req: AuthenticatedRequest, - ) { - return this.escrowService.proposeMilestoneChange( - escrowId, - conditionId, - dto, - this.getAuthenticatedUserId(req), - ); - } - - @Post(':id/conditions/:conditionId/accept') - @UseGuards(EscrowAccessGuard) - @ApiOperation({ summary: 'Accept a proposed change to a milestone' }) - async acceptMilestoneChange( - @Param('id') escrowId: string, - @Param('conditionId') conditionId: string, - @Request() req: AuthenticatedRequest, - ) { - return this.escrowService.acceptMilestoneChange( - escrowId, - conditionId, - this.getAuthenticatedUserId(req), - ); - } - - @Post(':id/conditions/:conditionId/release') - @UseGuards(EscrowAccessGuard) - @ApiOperation({ summary: 'Release a specific milestone payment' }) - async releaseMilestone( - @Param('id') escrowId: string, - @Param('conditionId') conditionId: string, - @Request() req: AuthenticatedRequest, - ) { - return this.escrowService.releaseMilestone( - escrowId, - conditionId, - this.getAuthenticatedUserId(req), - ); - } - - @Post(':id/parties/:partyId/accept') - @UseGuards(EscrowAccessGuard) - @ApiOperation({ summary: 'Accept a party invitation for an escrow' }) - async acceptPartyInvitation( - @Param('id') escrowId: string, - @Param('partyId') partyId: string, - @Request() req: AuthenticatedRequest, - ) { - const ipAddress = req.ip || req.socket?.remoteAddress; - return this.escrowService.acceptPartyInvitation( - escrowId, - partyId, - this.getAuthenticatedUserId(req), - ipAddress, - ); - } - - @Post(':id/parties/:partyId/reject') - @UseGuards(EscrowAccessGuard) - @ApiOperation({ summary: 'Reject a party invitation for an escrow' }) - async rejectPartyInvitation( - @Param('id') escrowId: string, - @Param('partyId') partyId: string, - @Request() req: AuthenticatedRequest, - ) { - const ipAddress = req.ip || req.socket?.remoteAddress; - return this.escrowService.rejectPartyInvitation( - escrowId, - partyId, - this.getAuthenticatedUserId(req), - ipAddress, - ); - } - - /** - * POST /escrows/:id/dispute - * File a dispute against an active escrow. Only a buyer or seller party may call this. - * Transitions the escrow from ACTIVE → DISPUTED and freezes fund release. - */ - @Post(':id/dispute') - @UseGuards(EscrowAccessGuard) - async fileDispute( - @Param('id') id: string, - @Body() dto: FileDisputeDto, - @Request() req: AuthenticatedRequest, - ) { - const ipAddress = req.ip || req.socket?.remoteAddress; - return this.escrowService.fileDispute( - id, - this.getAuthenticatedUserId(req), - dto, - ipAddress, - ); - } - - /** - * GET /escrows/:id/dispute - * Retrieve the dispute record for an escrow. Accessible to any party on the escrow. - */ - @Get(':id/dispute') - @UseGuards(EscrowAccessGuard) - async getDispute(@Param('id') id: string) { - return this.escrowService.getDispute(id); - } - - /** - * POST /escrows/:id/dispute/resolve - * Resolve an open dispute. Only an assigned arbitrator party may call this. - * Transitions the escrow from DISPUTED → COMPLETED (release/split) or CANCELLED (refund). - */ - @Post(':id/dispute/resolve') - @UseGuards(EscrowAccessGuard) - async resolveDispute( - @Param('id') id: string, - @Body() dto: ResolveDisputeDto, - @Request() req: AuthenticatedRequest, - ) { - const ipAddress = req.ip || req.socket?.remoteAddress; - return this.escrowService.resolveDispute( - id, - this.getAuthenticatedUserId(req), - dto, - ipAddress, - ); - } - /** - * POST /escrows/:id/evidence - * Upload evidence files for a dispute. Accepts up to 5 files, max 10MB each. - * Only dispute parties may call this. - */ - @Post(':id/evidence') - @UseGuards(EscrowAccessGuard) - @UseInterceptors(FilesInterceptor('files', 5)) - @ApiOperation({ summary: 'Upload evidence files for a dispute' }) - @ApiOkResponse({ type: UploadEvidenceResponseDto }) - async uploadEvidence( - @Param('id') id: string, - @Request() req: AuthenticatedRequest, - @UploadedFiles( - new ParseFilePipe({ - validators: [ - new MaxFileSizeValidator({ maxSize: 10 * 1024 * 1024 }), // 10MB per file - new FileTypeValidator({ - fileType: /(pdf|png|jpg|jpeg|doc|docx)$/, - }), - ], - }), - ) - files: Express.Multer.File[], - ): Promise { - const userId = this.getAuthenticatedUserId(req); - return this.evidenceService.uploadEvidence(id, files, userId); - } - - /** - * GET /escrows/:id/evidence - * Get all evidence file metadata for a dispute. Accessible to dispute parties. - */ - @Get(':id/evidence') - @UseGuards(EscrowAccessGuard) - @ApiOperation({ summary: 'Get evidence files for a dispute' }) - @ApiOkResponse({ type: [EvidenceFileMetadataDto] }) - async getEvidence( - @Param('id') id: string, - @Request() req: AuthenticatedRequest, - ): Promise { - const userId = this.getAuthenticatedUserId(req); - return this.evidenceService.getEvidence(id, userId); - } - - /** - * GET /escrows/:id/evidence/:cid - * Stream evidence file from IPFS by CID. Accessible to dispute parties. - */ - @Get(':id/evidence/:cid') - @UseGuards(EscrowAccessGuard) - @ApiOperation({ summary: 'Stream evidence file from IPFS' }) - async getEvidenceFile( - @Param('id') id: string, - @Param('cid') cid: string, - @Request() req: AuthenticatedRequest, - @Res() res: Response, - ): Promise { - const userId = this.getAuthenticatedUserId(req); - await this.evidenceService.getEvidenceFile(id, cid, userId, res); - } - - /** - * GET /escrows/:id/metadata - * Get escrow metadata from IPFS - */ - @Get(':id/metadata') - @UseGuards(EscrowAccessGuard) - @ApiOperation({ summary: 'Get escrow metadata from IPFS' }) - async getEscrowMetadata(@Param('id') id: string) { - return this.ipfsService.getMetadata(id); - } - - /** - * GET /escrows/:id/metadata/verify - * Verify escrow metadata integrity - */ - @Get(':id/metadata/verify') - @UseGuards(EscrowAccessGuard) - @ApiOperation({ summary: 'Verify escrow metadata integrity' }) - async verifyEscrowMetadata(@Param('id') id: string) { - return this.ipfsService.verifyMetadata(id); - } - - /** - * POST /escrows/:id/metadata/pin - * Pin escrow metadata to IPFS (admin only) - */ - @Post(':id/metadata/pin') - @UseGuards(EscrowAccessGuard, AdminGuard) - @ApiOperation({ summary: 'Pin escrow metadata to IPFS (admin only)' }) - async pinEscrowMetadata( - @Param('id') id: string, - @Body() metadata?: Record, - ) { - return this.ipfsService.pinMetadata(id, metadata || {}); - } -} diff --git a/apps/backend/src/modules/escrow/controllers/events.controller.ts b/apps/backend/src/modules/escrow/controllers/events.controller.ts deleted file mode 100644 index 406ca925..00000000 --- a/apps/backend/src/modules/escrow/controllers/events.controller.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Controller, Get, Query, UseGuards, Request } from '@nestjs/common'; -import { ThrottlerGuard } from '@nestjs/throttler'; -import { Request as ExpressRequest } from 'express'; -import { AuthGuard } from '../../auth/middleware/auth.guard'; -import { EscrowService } from '../services/escrow.service'; -import { ListEventsDto } from '../dto/list-events.dto'; - -interface AuthenticatedRequest extends ExpressRequest { - user: { userId: string; walletAddress: string }; -} - -@Controller('events') -@UseGuards(ThrottlerGuard, AuthGuard) -export class EventsController { - constructor(private readonly escrowService: EscrowService) {} - - @Get() - async findAllEvents( - @Query() query: ListEventsDto, - @Request() req: AuthenticatedRequest, - ) { - const userId = req.user.userId; - return this.escrowService.findEvents(userId, query); - } -} diff --git a/apps/backend/src/modules/escrow/dto/cancel-escrow.dto.ts b/apps/backend/src/modules/escrow/dto/cancel-escrow.dto.ts deleted file mode 100644 index 50e13045..00000000 --- a/apps/backend/src/modules/escrow/dto/cancel-escrow.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { IsString, IsOptional, MaxLength } from 'class-validator'; - -export class CancelEscrowDto { - @IsString() - @IsOptional() - @MaxLength(1000) - reason?: string; -} diff --git a/apps/backend/src/modules/escrow/dto/create-escrow.dto.ts b/apps/backend/src/modules/escrow/dto/create-escrow.dto.ts deleted file mode 100644 index 76f7c2b8..00000000 --- a/apps/backend/src/modules/escrow/dto/create-escrow.dto.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { - IsString, - IsNotEmpty, - IsOptional, - IsNumber, - IsPositive, - IsEnum, - IsArray, - ValidateNested, - IsDateString, - MaxLength, - ArrayMinSize, - ValidateIf, -} from 'class-validator'; -import { Type } from 'class-transformer'; -import { EscrowType } from '../entities/escrow.entity'; -import { PartyRole } from '../entities/party.entity'; -import { ConditionType } from '../entities/condition.entity'; - -export class EscrowAssetDto { - @IsString() - @IsNotEmpty() - code: string; - - @ValidateIf((o: EscrowAssetDto) => o.code !== 'XLM') - @IsString() - @IsNotEmpty() - issuer: string; -} - -export class CreatePartyDto { - @IsString() - @IsNotEmpty() - userId: string; - - @IsEnum(PartyRole) - role: PartyRole; -} - -export class CreateConditionDto { - @IsString() - @IsNotEmpty() - @MaxLength(1000) - description: string; - - @IsEnum(ConditionType) - @IsOptional() - type?: ConditionType; - - @IsOptional() - metadata?: Record; -} - -export class CreateEscrowDto { - @IsString() - @IsNotEmpty() - @MaxLength(255) - title: string; - - @IsString() - @IsOptional() - @MaxLength(2000) - description?: string; - - @IsNumber() - @IsPositive() - amount: number; - - @IsOptional() - @ValidateNested() - @Type(() => EscrowAssetDto) - asset?: EscrowAssetDto; - - @IsEnum(EscrowType) - @IsOptional() - type?: EscrowType; - - @IsArray() - @ArrayMinSize(1) - @ValidateNested({ each: true }) - @Type(() => CreatePartyDto) - parties: CreatePartyDto[]; - - @IsArray() - @IsOptional() - @ValidateNested({ each: true }) - @Type(() => CreateConditionDto) - conditions?: CreateConditionDto[]; - - @IsDateString() - @IsOptional() - expiresAt?: string; - - @IsString() - @IsOptional() - metadataHash?: string; -} diff --git a/apps/backend/src/modules/escrow/dto/dispute.dto.ts b/apps/backend/src/modules/escrow/dto/dispute.dto.ts deleted file mode 100644 index 0ec7f2cc..00000000 --- a/apps/backend/src/modules/escrow/dto/dispute.dto.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - IsString, - IsNotEmpty, - MaxLength, - IsArray, - IsOptional, - IsEnum, - IsNumber, - Min, - Max, -} from 'class-validator'; -import { DisputeOutcome } from '../entities/dispute.entity'; - -export class FileDisputeDto { - @IsString() - @IsNotEmpty() - @MaxLength(2000) - reason: string; - - /** - * Optional list of evidence URLs or reference strings (e.g. IPFS CIDs, - * cloud storage links, transaction hashes). - */ - @IsArray() - @IsOptional() - @IsString({ each: true }) - @MaxLength(500, { each: true }) - evidence?: string[]; -} - -export class ResolveDisputeDto { - @IsEnum(DisputeOutcome) - outcome: DisputeOutcome; - - @IsString() - @IsNotEmpty() - @MaxLength(2000) - resolutionNotes: string; - - /** - * Percentage of funds to release to the seller (0-100). - * Required when outcome is SPLIT; sellerPercent + buyerPercent must equal 100. - */ - @IsNumber() - @Min(0) - @Max(100) - @IsOptional() - sellerPercent?: number; - - /** - * Percentage of funds to refund to the buyer (0-100). - * Required when outcome is SPLIT; sellerPercent + buyerPercent must equal 100. - */ - @IsNumber() - @Min(0) - @Max(100) - @IsOptional() - buyerPercent?: number; -} diff --git a/apps/backend/src/modules/escrow/dto/escrow-overview.dto.ts b/apps/backend/src/modules/escrow/dto/escrow-overview.dto.ts deleted file mode 100644 index 5e46aa6d..00000000 --- a/apps/backend/src/modules/escrow/dto/escrow-overview.dto.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { Type } from 'class-transformer'; -import { - IsDateString, - IsEnum, - IsInt, - IsOptional, - IsString, - Max, - Min, -} from 'class-validator'; -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; - -export enum EscrowOverviewRole { - DEPOSITOR = 'depositor', - RECIPIENT = 'recipient', - ANY = 'any', -} - -export enum EscrowOverviewStatus { - CREATED = 'created', - ACTIVE = 'active', - COMPLETED = 'completed', - CANCELLED = 'cancelled', - DISPUTED = 'disputed', - EXPIRED = 'expired', - PENDING = 'pending', -} - -export enum EscrowOverviewSortBy { - CREATED_AT = 'createdAt', - DEADLINE = 'deadline', -} - -export enum EscrowOverviewSortOrder { - ASC = 'asc', - DESC = 'desc', -} - -export class EscrowOverviewQueryDto { - @ApiPropertyOptional({ - enum: EscrowOverviewRole, - default: EscrowOverviewRole.ANY, - }) - @IsEnum(EscrowOverviewRole) - @IsOptional() - role?: EscrowOverviewRole = EscrowOverviewRole.ANY; - - @ApiPropertyOptional({ enum: EscrowOverviewStatus }) - @IsEnum(EscrowOverviewStatus) - @IsOptional() - status?: EscrowOverviewStatus; - - @ApiPropertyOptional({ - description: 'Asset/token identifier', - example: 'XLM', - }) - @IsString() - @IsOptional() - token?: string; - - @ApiPropertyOptional({ - description: 'Filter from created date (inclusive)', - example: '2026-01-01T00:00:00.000Z', - }) - @IsDateString() - @IsOptional() - from?: string; - - @ApiPropertyOptional({ - description: 'Filter to created date (inclusive)', - example: '2026-12-31T23:59:59.999Z', - }) - @IsDateString() - @IsOptional() - to?: string; - - @ApiPropertyOptional({ minimum: 1, default: 1 }) - @Type(() => Number) - @IsInt() - @Min(1) - @IsOptional() - page?: number = 1; - - @ApiPropertyOptional({ minimum: 1, maximum: 100, default: 20 }) - @Type(() => Number) - @IsInt() - @Min(1) - @Max(100) - @IsOptional() - pageSize?: number = 20; - - @ApiPropertyOptional({ - enum: EscrowOverviewSortBy, - default: EscrowOverviewSortBy.CREATED_AT, - }) - @IsEnum(EscrowOverviewSortBy) - @IsOptional() - sortBy?: EscrowOverviewSortBy = EscrowOverviewSortBy.CREATED_AT; - - @ApiPropertyOptional({ - enum: EscrowOverviewSortOrder, - default: EscrowOverviewSortOrder.DESC, - }) - @IsEnum(EscrowOverviewSortOrder) - @IsOptional() - sortOrder?: EscrowOverviewSortOrder = EscrowOverviewSortOrder.DESC; -} - -export class EscrowOverviewItemDto { - @ApiProperty() - escrowId: string; - - @ApiProperty() - depositor: string; - - @ApiProperty({ nullable: true }) - recipient: string | null; - - @ApiProperty() - token: string; - - @ApiPropertyOptional() - tokenIssuer?: string; - - @ApiProperty() - tokenDecimals: number; - - @ApiProperty() - totalAmount: number; - - @ApiProperty() - totalReleased: number; - - @ApiProperty() - remainingAmount: number; - - @ApiProperty() - status: string; - - @ApiProperty({ nullable: true }) - deadline: Date | null; - - @ApiProperty() - createdAt: Date; - - @ApiProperty() - updatedAt: Date; -} - -export class EscrowOverviewResponseDto { - @ApiProperty({ type: [EscrowOverviewItemDto] }) - data: EscrowOverviewItemDto[]; - - @ApiProperty() - totalItems: number; - - @ApiProperty() - totalPages: number; - - @ApiProperty() - page: number; - - @ApiProperty() - pageSize: number; -} diff --git a/apps/backend/src/modules/escrow/dto/event-response.dto.ts b/apps/backend/src/modules/escrow/dto/event-response.dto.ts deleted file mode 100644 index 926c9415..00000000 --- a/apps/backend/src/modules/escrow/dto/event-response.dto.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { EscrowEventType } from '../entities/escrow-event.entity'; - -export class EventResponseDto { - id: string; - escrowId: string; - eventType: EscrowEventType; - actorId?: string; - data?: Record; - ipAddress?: string; - createdAt: Date; - cursor: string; // Monotonic cursor for incremental sync - - // Escrow details for context - escrow?: { - id: string; - title: string; - amount: number; - assetCode: string; - assetIssuer?: string; - status: string; - }; - - // Actor details (wallet address) - actor?: { - walletAddress?: string; - }; -} diff --git a/apps/backend/src/modules/escrow/dto/expire-escrow.dto.ts b/apps/backend/src/modules/escrow/dto/expire-escrow.dto.ts deleted file mode 100644 index 53e22e5e..00000000 --- a/apps/backend/src/modules/escrow/dto/expire-escrow.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { IsString, IsOptional, MaxLength } from 'class-validator'; - -export class ExpireEscrowDto { - @IsString() - @IsOptional() - @MaxLength(1000) - reason?: string; -} diff --git a/apps/backend/src/modules/escrow/dto/fulfill-condition.dto.ts b/apps/backend/src/modules/escrow/dto/fulfill-condition.dto.ts deleted file mode 100644 index fd096ffd..00000000 --- a/apps/backend/src/modules/escrow/dto/fulfill-condition.dto.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { IsString, IsOptional, MaxLength } from 'class-validator'; - -export class FulfillConditionDto { - @IsString() - @IsOptional() - @MaxLength(2000) - notes?: string; - - @IsString() - @IsOptional() - @MaxLength(500) - evidence?: string; -} diff --git a/apps/backend/src/modules/escrow/dto/fund-escrow.dto.ts b/apps/backend/src/modules/escrow/dto/fund-escrow.dto.ts deleted file mode 100644 index cd2e6ab7..00000000 --- a/apps/backend/src/modules/escrow/dto/fund-escrow.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IsNumber, IsPositive } from 'class-validator'; - -export class FundEscrowDto { - @IsNumber() - @IsPositive() - amount: number; -} diff --git a/apps/backend/src/modules/escrow/dto/list-escrows.dto.ts b/apps/backend/src/modules/escrow/dto/list-escrows.dto.ts deleted file mode 100644 index b716a80b..00000000 --- a/apps/backend/src/modules/escrow/dto/list-escrows.dto.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { IsString, IsOptional, IsInt, Min, Max, IsEnum } from 'class-validator'; -import { Type } from 'class-transformer'; -import { EscrowStatus, EscrowType } from '../entities/escrow.entity'; -import { PartyRole } from '../entities/party.entity'; - -export enum SortBy { - CREATED_AT = 'createdAt', - UPDATED_AT = 'updatedAt', - AMOUNT = 'amount', - TITLE = 'title', -} - -export enum SortOrder { - ASC = 'ASC', - DESC = 'DESC', -} - -export class ListEscrowsDto { - @IsInt() - @Min(1) - @IsOptional() - @Type(() => Number) - page?: number = 1; - - @IsInt() - @Min(1) - @Max(100) - @IsOptional() - @Type(() => Number) - limit?: number = 10; - - @IsEnum(EscrowStatus) - @IsOptional() - status?: EscrowStatus; - - @IsEnum(EscrowType) - @IsOptional() - type?: EscrowType; - - @IsEnum(PartyRole) - @IsOptional() - role?: PartyRole; - - @IsEnum(SortBy) - @IsOptional() - sortBy?: SortBy = SortBy.CREATED_AT; - - @IsEnum(SortOrder) - @IsOptional() - sortOrder?: SortOrder = SortOrder.DESC; - - @IsString() - @IsOptional() - search?: string; - - @IsString() - @IsOptional() - assetCode?: string; - - @IsString() - @IsOptional() - assetIssuer?: string; -} diff --git a/apps/backend/src/modules/escrow/dto/list-events.dto.ts b/apps/backend/src/modules/escrow/dto/list-events.dto.ts deleted file mode 100644 index 568c6d3a..00000000 --- a/apps/backend/src/modules/escrow/dto/list-events.dto.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { - IsString, - IsOptional, - IsInt, - Min, - Max, - IsEnum, - IsDateString, - IsUUID, -} from 'class-validator'; -import { Type } from 'class-transformer'; -import { EscrowEventType } from '../entities/escrow-event.entity'; - -export enum EventSortBy { - CREATED_AT = 'createdAt', - EVENT_TYPE = 'eventType', - CURSOR = 'cursor', -} - -export enum EventSortOrder { - ASC = 'ASC', - DESC = 'DESC', -} - -export class ListEventsDto { - @IsInt() - @Min(1) - @IsOptional() - @Type(() => Number) - page?: number = 1; - - @IsInt() - @Min(1) - @Max(100) - @IsOptional() - @Type(() => Number) - limit?: number = 10; - - @IsEnum(EscrowEventType) - @IsOptional() - eventType?: EscrowEventType; - - @IsString() - @IsOptional() - actorId?: string; - - @IsDateString() - @IsOptional() - dateFrom?: string; - - @IsDateString() - @IsOptional() - dateTo?: string; - - @IsEnum(EventSortBy) - @IsOptional() - sortBy?: EventSortBy = EventSortBy.CREATED_AT; - - @IsUUID() - @IsOptional() - escrowId?: string; - - @IsEnum(EventSortOrder) - @IsOptional() - sortOrder?: EventSortOrder = EventSortOrder.DESC; - - // Cursor-based pagination for incremental sync - @IsString() - @IsOptional() - cursor?: string; - - // When using cursor, fetch events after this cursor - @IsString() - @IsOptional() - after?: string; - - // When using cursor, fetch events before this cursor - @IsString() - @IsOptional() - before?: string; -} diff --git a/apps/backend/src/modules/escrow/dto/milestone-change.dto.ts b/apps/backend/src/modules/escrow/dto/milestone-change.dto.ts deleted file mode 100644 index ae3e95bd..00000000 --- a/apps/backend/src/modules/escrow/dto/milestone-change.dto.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ApiPropertyOptional } from '@nestjs/swagger'; -import { IsOptional, IsString, IsNumber, Min } from 'class-validator'; - -export class ProposeMilestoneChangeDto { - @ApiPropertyOptional({ - description: 'The proposed new amount for this milestone', - example: 100.5, - }) - @IsOptional() - @IsNumber() - @Min(0) - amount?: number; - - @ApiPropertyOptional({ - description: 'The proposed new description for this milestone', - example: 'Deliver the first draft of the integration module', - }) - @IsOptional() - @IsString() - description?: string; -} diff --git a/apps/backend/src/modules/escrow/dto/update-escrow.dto.ts b/apps/backend/src/modules/escrow/dto/update-escrow.dto.ts deleted file mode 100644 index c171488d..00000000 --- a/apps/backend/src/modules/escrow/dto/update-escrow.dto.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IsString, IsOptional, IsDateString, MaxLength } from 'class-validator'; - -export class UpdateEscrowDto { - @IsString() - @IsOptional() - @MaxLength(255) - title?: string; - - @IsString() - @IsOptional() - @MaxLength(2000) - description?: string; - - @IsDateString() - @IsOptional() - expiresAt?: string; -} diff --git a/apps/backend/src/modules/escrow/dto/upload-evidence.dto.ts b/apps/backend/src/modules/escrow/dto/upload-evidence.dto.ts deleted file mode 100644 index 66aeae4a..00000000 --- a/apps/backend/src/modules/escrow/dto/upload-evidence.dto.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { IsString, IsNumber, IsNotEmpty } from 'class-validator'; - -export class EvidenceFileMetadataDto { - @IsString() - @IsNotEmpty() - cid: string; - - @IsString() - @IsNotEmpty() - name: string; - - @IsString() - @IsNotEmpty() - type: string; - - @IsNumber() - @IsNotEmpty() - size: number; - - @IsString() - @IsNotEmpty() - uploadedAt: string; - - @IsString() - @IsNotEmpty() - uploadedBy: string; -} - -export class UploadEvidenceResponseDto { - @IsString() - @IsNotEmpty() - escrowId: string; - - @IsString() - @IsNotEmpty() - disputeId: string; - - uploadedFiles: EvidenceFileMetadataDto[]; - - @IsString() - @IsNotEmpty() - message: string; -} diff --git a/apps/backend/src/modules/escrow/entities/condition.entity.ts b/apps/backend/src/modules/escrow/entities/condition.entity.ts deleted file mode 100644 index ab230529..00000000 --- a/apps/backend/src/modules/escrow/entities/condition.entity.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ManyToOne, - JoinColumn, -} from 'typeorm'; -import { Escrow } from './escrow.entity'; - -export enum ConditionType { - MANUAL = 'manual', - TIME_BASED = 'time_based', - ORACLE = 'oracle', -} - -@Entity('escrow_conditions') -export class Condition { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - escrowId: string; - - @ManyToOne(() => Escrow, (escrow) => escrow.conditions, { - onDelete: 'CASCADE', - }) - @JoinColumn({ name: 'escrowId' }) - escrow: Escrow; - - @Column({ type: 'text' }) - description: string; - - @Column({ - type: 'varchar', - default: ConditionType.MANUAL, - }) - type: ConditionType; - - @Column({ default: false }) - isFulfilled: boolean; - - @Column({ type: 'datetime', nullable: true }) - fulfilledAt?: Date; - - @Column({ nullable: true }) - fulfilledByUserId?: string; - - @Column({ type: 'text', nullable: true }) - fulfillmentNotes?: string; - - @Column({ type: 'text', nullable: true }) - fulfillmentEvidence?: string; - - @Column({ default: false }) - isMet: boolean; - - @Column({ type: 'datetime', nullable: true }) - metAt?: Date; - - @Column({ nullable: true }) - metByUserId?: string; - - @Column({ type: 'simple-json', nullable: true }) - metadata?: Record; - - @Column({ type: 'decimal', precision: 18, scale: 7, nullable: true }) - amount?: number; - - @Column({ type: 'decimal', precision: 18, scale: 7, nullable: true }) - proposedAmount?: number; - - @Column({ type: 'text', nullable: true }) - proposedDescription?: string; - - @Column({ nullable: true }) - proposedByUserId?: string; - - @Column({ default: false }) - isReleased: boolean; - - @Column({ type: 'datetime', nullable: true }) - releasedAt?: Date; - - @CreateDateColumn() - createdAt: Date; - - @UpdateDateColumn() - updatedAt: Date; -} diff --git a/apps/backend/src/modules/escrow/entities/dispute.entity.ts b/apps/backend/src/modules/escrow/entities/dispute.entity.ts deleted file mode 100644 index 8f979661..00000000 --- a/apps/backend/src/modules/escrow/entities/dispute.entity.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ManyToOne, - OneToOne, - JoinColumn, -} from 'typeorm'; -import { Escrow } from './escrow.entity'; -import { User } from '../../user/entities/user.entity'; - -export enum DisputeStatus { - OPEN = 'open', - UNDER_REVIEW = 'under_review', - RESOLVED = 'resolved', -} - -export enum DisputeOutcome { - RELEASED_TO_SELLER = 'released_to_seller', - REFUNDED_TO_BUYER = 'refunded_to_buyer', - SPLIT = 'split', -} - -@Entity('disputes') -export class Dispute { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - escrowId: string; - - @OneToOne(() => Escrow, { onDelete: 'CASCADE' }) - @JoinColumn({ name: 'escrowId' }) - escrow: Escrow; - - @Column() - filedByUserId: string; - - @ManyToOne(() => User) - @JoinColumn({ name: 'filedByUserId' }) - filedBy: User; - - @Column({ type: 'text' }) - reason: string; - - // Stores URLs or reference strings pointing to supporting evidence - @Column({ type: 'simple-json', nullable: true }) - evidence: string[] | null; - - // Evidence files with metadata stored on IPFS - @Column({ type: 'simple-json', nullable: true, default: () => "'[]'" }) - evidenceFiles: Array<{ - cid: string; - name: string; - type: string; - size: number; - uploadedAt: string; - uploadedBy: string; - }> | null; - - @Column({ type: 'varchar', default: DisputeStatus.OPEN }) - status: DisputeStatus; - - @Column({ nullable: true }) - resolvedByUserId: string | null; - - @ManyToOne(() => User, { nullable: true }) - @JoinColumn({ name: 'resolvedByUserId' }) - resolvedBy: User | null; - - @Column({ type: 'text', nullable: true }) - resolutionNotes: string | null; - - // Percentage of funds to release to seller (0-100). Required when outcome is SPLIT. - @Column({ type: 'decimal', precision: 5, scale: 2, nullable: true }) - sellerPercent: number | null; - - // Percentage of funds to refund to buyer (0-100). Required when outcome is SPLIT. - @Column({ type: 'decimal', precision: 5, scale: 2, nullable: true }) - buyerPercent: number | null; - - @Column({ type: 'varchar', nullable: true }) - outcome: DisputeOutcome | null; - - @Column({ type: 'datetime', nullable: true }) - resolvedAt: Date | null; - - @CreateDateColumn() - createdAt: Date; - - @UpdateDateColumn() - updatedAt: Date; -} diff --git a/apps/backend/src/modules/escrow/entities/escrow-event.entity.ts b/apps/backend/src/modules/escrow/entities/escrow-event.entity.ts deleted file mode 100644 index 77177c9b..00000000 --- a/apps/backend/src/modules/escrow/entities/escrow-event.entity.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - ManyToOne, - JoinColumn, - Index, - Generated, -} from 'typeorm'; -import { Escrow } from './escrow.entity'; - -export enum EscrowEventType { - CREATED = 'created', - PARTY_ADDED = 'party_added', - PARTY_ACCEPTED = 'party_accepted', - PARTY_REJECTED = 'party_rejected', - FUNDED = 'funded', - CONDITION_FULFILLED = 'condition_fulfilled', - CONDITION_MET = 'condition_met', - STATUS_CHANGED = 'status_changed', - UPDATED = 'updated', - CANCELLED = 'cancelled', - COMPLETED = 'completed', - DISPUTED = 'disputed', - DISPUTE_FILED = 'dispute_filed', - DISPUTE_RESOLVED = 'dispute_resolved', - EXPIRED = 'expired', - EXPIRATION_WARNING_SENT = 'expiration_warning_sent', - MILESTONE_RELEASED = 'milestone_released', -} - -@Entity('escrow_events') -export class EscrowEvent { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - escrowId: string; - - @ManyToOne(() => Escrow, (escrow) => escrow.events, { onDelete: 'CASCADE' }) - @JoinColumn({ name: 'escrowId' }) - escrow: Escrow; - - @Column({ - type: 'varchar', - }) - eventType: EscrowEventType; - - @Column({ nullable: true }) - actorId?: string; - - @Column({ type: 'simple-json', nullable: true }) - data?: Record; - - @Column({ nullable: true }) - ipAddress?: string; - - @CreateDateColumn() - createdAt: Date; - - // Monotonic cursor for incremental sync - // Auto-incrementing sequence number for ordering events - @Column({ type: 'bigint', name: 'cursor' }) - @Generated('increment') - @Index() - cursor: string; -} diff --git a/apps/backend/src/modules/escrow/entities/escrow.entity.ts b/apps/backend/src/modules/escrow/entities/escrow.entity.ts deleted file mode 100644 index 223d4257..00000000 --- a/apps/backend/src/modules/escrow/entities/escrow.entity.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - Entity, - Index, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ManyToOne, - OneToMany, - JoinColumn, -} from 'typeorm'; -import { User } from '../../user/entities/user.entity'; -import { Party } from './party.entity'; -import { Condition } from './condition.entity'; -import { EscrowEvent } from './escrow-event.entity'; - -export enum EscrowStatus { - PENDING = 'pending', - ACTIVE = 'active', - COMPLETED = 'completed', - CANCELLED = 'cancelled', - DISPUTED = 'disputed', - EXPIRED = 'expired', -} - -export enum EscrowType { - STANDARD = 'standard', - MILESTONE = 'milestone', - TIMED = 'timed', -} - -@Entity('escrows') -@Index('idx_escrows_creator', ['creatorId']) -@Index('idx_escrows_status', ['status']) -@Index('idx_escrows_asset', ['assetCode', 'assetIssuer']) -@Index('idx_escrows_created_at', ['createdAt']) -@Index('idx_escrows_expires_at', ['expiresAt']) -@Index('idx_escrows_creator_status_created', [ - 'creatorId', - 'status', - 'createdAt', -]) -export class Escrow { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - title: string; - - @Column({ type: 'text', nullable: true }) - description?: string; - - @Column({ type: 'decimal', precision: 18, scale: 7 }) - amount: number; - - @Column({ type: 'decimal', precision: 18, scale: 7, default: 0 }) - releasedAmount: number; - - @Column({ default: 'XLM', name: 'asset_code' }) - assetCode: string; - - @Column({ nullable: true, name: 'asset_issuer' }) - assetIssuer?: string; - - @Column({ - type: 'varchar', - default: EscrowStatus.PENDING, - }) - status: EscrowStatus; - - @Column({ - type: 'varchar', - default: EscrowType.STANDARD, - }) - type: EscrowType; - - @Column() - creatorId: string; - - @ManyToOne(() => User) - @JoinColumn({ name: 'creatorId' }) - creator: User; - - @Column({ nullable: true }) - releaseTransactionHash?: string; - - @Column({ nullable: true }) - stellarTxHash?: string; - - @Column({ type: 'datetime', nullable: true }) - fundedAt?: Date; - - @Column({ default: false }) - isReleased: boolean; - - @Column({ type: 'datetime', nullable: true }) - expiresAt?: Date; - - @Column({ type: 'datetime', nullable: true }) - expirationNotifiedAt?: Date; - - @Column({ default: true }) - isActive: boolean; - - @OneToMany(() => Party, (party) => party.escrow, { cascade: true }) - parties: Party[]; - - @OneToMany(() => Condition, (condition) => condition.escrow, { - cascade: true, - }) - conditions: Condition[]; - - @OneToMany(() => EscrowEvent, (event) => event.escrow, { cascade: true }) - events: EscrowEvent[]; - - @Column({ nullable: true }) - metadataHash?: string; - - @Column({ nullable: true, name: 'ipfs_cid' }) - ipfsCid?: string; - - @Column({ nullable: true, name: 'ipfs_metadata_hash' }) - ipfsMetadataHash?: string; - - @Column({ type: 'int', default: 0, name: 'ipfs_version' }) - ipfsVersion?: number; - - @CreateDateColumn() - createdAt: Date; - - @UpdateDateColumn() - updatedAt: Date; -} diff --git a/apps/backend/src/modules/escrow/entities/party.entity.ts b/apps/backend/src/modules/escrow/entities/party.entity.ts deleted file mode 100644 index 3fc36ad9..00000000 --- a/apps/backend/src/modules/escrow/entities/party.entity.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - Entity, - Index, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - ManyToOne, - JoinColumn, -} from 'typeorm'; -import { Escrow } from './escrow.entity'; -import { User } from '../../user/entities/user.entity'; - -export enum PartyRole { - BUYER = 'buyer', - SELLER = 'seller', - ARBITRATOR = 'arbitrator', -} - -export enum PartyStatus { - PENDING = 'pending', - ACCEPTED = 'accepted', - REJECTED = 'rejected', -} - -@Entity('escrow_parties') -@Index('idx_escrow_parties_user_role', ['userId', 'role']) -@Index('idx_escrow_parties_escrow_role', ['escrowId', 'role']) -export class Party { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - escrowId: string; - - @ManyToOne(() => Escrow, (escrow) => escrow.parties, { onDelete: 'CASCADE' }) - @JoinColumn({ name: 'escrowId' }) - escrow: Escrow; - - @Column() - userId: string; - - @ManyToOne(() => User) - @JoinColumn({ name: 'userId' }) - user: User; - - @Column({ - type: 'varchar', - }) - role: PartyRole; - - @Column({ - type: 'varchar', - default: PartyStatus.PENDING, - }) - status: PartyStatus; - - @Column({ type: 'datetime', nullable: true }) - respondedAt: Date | null; - - @CreateDateColumn() - createdAt: Date; -} diff --git a/apps/backend/src/modules/escrow/escrow-dispute.service.ts b/apps/backend/src/modules/escrow/escrow-dispute.service.ts deleted file mode 100644 index 5607cab4..00000000 --- a/apps/backend/src/modules/escrow/escrow-dispute.service.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Injectable, ConflictException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; - -import { Dispute, DisputeStatus } from './entities/dispute.entity'; -import { Escrow, EscrowStatus } from './entities/escrow.entity'; -import { validateTransition } from './escrow-state-machine'; -import { DisputeOutcome } from './entities/dispute.entity'; - -@Injectable() -export class EscrowDisputeService { - constructor( - @InjectRepository(Dispute) - private disputeRepo: Repository, - @InjectRepository(Escrow) - private escrowRepo: Repository, - ) {} - - async fileDispute(escrow: Escrow, userId: string, reason: string) { - if (escrow.status !== EscrowStatus.ACTIVE) { - throw new ConflictException('Cannot dispute this escrow'); - } - - validateTransition(escrow.status, EscrowStatus.DISPUTED); - - escrow.status = EscrowStatus.DISPUTED; - await this.escrowRepo.save(escrow); - - return this.disputeRepo.save({ - escrowId: escrow.id, - initiatorUserId: userId, - reason, - status: DisputeStatus.OPEN, - }); - } - - async resolve(dispute: Dispute, outcome: DisputeOutcome) { - dispute.status = DisputeStatus.RESOLVED; - dispute.outcome = outcome; - - return this.disputeRepo.save(dispute); - } -} diff --git a/apps/backend/src/modules/escrow/escrow-funding.service.ts b/apps/backend/src/modules/escrow/escrow-funding.service.ts deleted file mode 100644 index 8b851944..00000000 --- a/apps/backend/src/modules/escrow/escrow-funding.service.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Injectable, ConflictException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; - -import { Escrow, EscrowStatus } from './entities/escrow.entity'; -import { validateTransition } from './escrow-state-machine'; - -@Injectable() -export class EscrowFundingService { - constructor( - @InjectRepository(Escrow) - private escrowRepo: Repository, - ) {} - - async fund(escrow: Escrow) { - if (escrow.status !== EscrowStatus.PENDING) { - throw new ConflictException('Escrow not fundable'); - } - - validateTransition(escrow.status, EscrowStatus.ACTIVE); - - escrow.status = EscrowStatus.ACTIVE; - return this.escrowRepo.save(escrow); - } - - async release(escrow: Escrow) { - validateTransition(escrow.status, EscrowStatus.COMPLETED); - - escrow.status = EscrowStatus.COMPLETED; - escrow.isReleased = true; - - return this.escrowRepo.save(escrow); - } - - async refund(escrow: Escrow) { - validateTransition(escrow.status, EscrowStatus.CANCELLED); - - escrow.status = EscrowStatus.CANCELLED; - - return this.escrowRepo.save(escrow); - } -} diff --git a/apps/backend/src/modules/escrow/escrow-lifecycle.service.ts b/apps/backend/src/modules/escrow/escrow-lifecycle.service.ts deleted file mode 100644 index c0d2e0e7..00000000 --- a/apps/backend/src/modules/escrow/escrow-lifecycle.service.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; - -import { Escrow, EscrowStatus } from './entities/escrow.entity'; -import { Party } from './entities/party.entity'; -import { Condition } from './entities/condition.entity'; -import { EscrowEvent, EscrowEventType } from './entities/escrow-event.entity'; -import { CreateEscrowDto } from './dto/create-escrow.dto'; -import { validateTransition } from './escrow-state-machine'; - -@Injectable() -export class EscrowLifecycleService { - constructor( - @InjectRepository(Escrow) - private escrowRepo: Repository, - @InjectRepository(Party) - private partyRepo: Repository, - @InjectRepository(Condition) - private conditionRepo: Repository, - @InjectRepository(EscrowEvent) - private eventRepo: Repository, - ) {} - - async create(dto: CreateEscrowDto, creatorId: string): Promise { - const escrow = this.escrowRepo.create({ - ...dto, - creatorId, - status: EscrowStatus.PENDING, - }); - - const saved = await this.escrowRepo.save(escrow); - - await this.partyRepo.save( - dto.parties.map((p) => - this.partyRepo.create({ ...p, escrowId: saved.id }), - ), - ); - - if (dto.conditions) { - await this.conditionRepo.save( - dto.conditions.map((c) => - this.conditionRepo.create({ ...c, escrowId: saved.id }), - ), - ); - } - - await this.logEvent(saved.id, EscrowEventType.CREATED, creatorId); - - return saved; - } - - async cancel(escrow: Escrow, userId: string): Promise { - validateTransition(escrow.status, EscrowStatus.CANCELLED); - - escrow.status = EscrowStatus.CANCELLED; - const saved = await this.escrowRepo.save(escrow); - - await this.logEvent(saved.id, EscrowEventType.CANCELLED, userId); - - return saved; - } - - async expire(escrow: Escrow): Promise { - validateTransition(escrow.status, EscrowStatus.EXPIRED); - - escrow.status = EscrowStatus.EXPIRED; - const saved = await this.escrowRepo.save(escrow); - - await this.logEvent(saved.id, EscrowEventType.EXPIRED); - - return saved; - } - - private async logEvent( - escrowId: string, - type: EscrowEventType, - actorId?: string, - ): Promise { - const event = this.eventRepo.create({ - escrow: { id: escrowId }, - eventType: type, - actorId, - }); - - await this.eventRepo.save(event); - } -} diff --git a/apps/backend/src/modules/escrow/escrow-query.service.ts b/apps/backend/src/modules/escrow/escrow-query.service.ts deleted file mode 100644 index c77c21a9..00000000 --- a/apps/backend/src/modules/escrow/escrow-query.service.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Injectable, NotFoundException } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; - -import { Escrow } from './entities/escrow.entity'; - -@Injectable() -export class EscrowQueryService { - constructor( - @InjectRepository(Escrow) - private escrowRepo: Repository, - ) {} - - async findOne(id: string): Promise { - const escrow = await this.escrowRepo.findOne({ - where: { id }, - relations: ['parties', 'conditions'], - }); - - if (!escrow) throw new NotFoundException('Escrow not found'); - - return escrow; - } - - async findAll(userId: string) { - return this.escrowRepo.find({ - where: [{ creatorId: userId }], - order: { createdAt: 'DESC' }, - }); - } -} diff --git a/apps/backend/src/modules/escrow/escrow-state-machine.spec.ts b/apps/backend/src/modules/escrow/escrow-state-machine.spec.ts deleted file mode 100644 index 1b7dfac6..00000000 --- a/apps/backend/src/modules/escrow/escrow-state-machine.spec.ts +++ /dev/null @@ -1,182 +0,0 @@ -/** - * Escrow State Transition Table: - * - * Current Status | Allowed New Statuses - * ----------------|--------------------- - * PENDING | ACTIVE, CANCELLED, EXPIRED - * ACTIVE | COMPLETED, CANCELLED, DISPUTED, EXPIRED - * DISPUTED | COMPLETED, CANCELLED, EXPIRED - * COMPLETED | (None) - * CANCELLED | (None) - * EXPIRED | (None) - */ - -import { BadRequestException } from '@nestjs/common'; -import { EscrowStatus } from './entities/escrow.entity'; -import { - canTransition, - validateTransition, - isTerminalStatus, -} from './escrow-state-machine'; - -describe('EscrowStateMachine', () => { - describe('canTransition', () => { - it('should allow PENDING -> ACTIVE', () => { - expect(canTransition(EscrowStatus.PENDING, EscrowStatus.ACTIVE)).toBe( - true, - ); - }); - - it('should allow PENDING -> CANCELLED', () => { - expect(canTransition(EscrowStatus.PENDING, EscrowStatus.CANCELLED)).toBe( - true, - ); - }); - - it('should allow ACTIVE -> COMPLETED', () => { - expect(canTransition(EscrowStatus.ACTIVE, EscrowStatus.COMPLETED)).toBe( - true, - ); - }); - - it('should allow ACTIVE -> CANCELLED', () => { - expect(canTransition(EscrowStatus.ACTIVE, EscrowStatus.CANCELLED)).toBe( - true, - ); - }); - - it('should allow ACTIVE -> DISPUTED', () => { - expect(canTransition(EscrowStatus.ACTIVE, EscrowStatus.DISPUTED)).toBe( - true, - ); - }); - - it('should allow DISPUTED -> COMPLETED', () => { - expect(canTransition(EscrowStatus.DISPUTED, EscrowStatus.COMPLETED)).toBe( - true, - ); - }); - - it('should allow DISPUTED -> CANCELLED', () => { - expect(canTransition(EscrowStatus.DISPUTED, EscrowStatus.CANCELLED)).toBe( - true, - ); - }); - - it('should not allow transitions from COMPLETED', () => { - expect(canTransition(EscrowStatus.COMPLETED, EscrowStatus.PENDING)).toBe( - false, - ); - expect(canTransition(EscrowStatus.COMPLETED, EscrowStatus.ACTIVE)).toBe( - false, - ); - expect( - canTransition(EscrowStatus.COMPLETED, EscrowStatus.CANCELLED), - ).toBe(false); - }); - - it('should not allow transitions from CANCELLED', () => { - expect(canTransition(EscrowStatus.CANCELLED, EscrowStatus.PENDING)).toBe( - false, - ); - expect(canTransition(EscrowStatus.CANCELLED, EscrowStatus.ACTIVE)).toBe( - false, - ); - expect( - canTransition(EscrowStatus.CANCELLED, EscrowStatus.COMPLETED), - ).toBe(false); - }); - - it('should not allow PENDING -> COMPLETED directly', () => { - expect(canTransition(EscrowStatus.PENDING, EscrowStatus.COMPLETED)).toBe( - false, - ); - }); - - it('should not allow PENDING -> DISPUTED directly', () => { - expect(canTransition(EscrowStatus.PENDING, EscrowStatus.DISPUTED)).toBe( - false, - ); - }); - - it('should allow PENDING -> EXPIRED', () => { - expect(canTransition(EscrowStatus.PENDING, EscrowStatus.EXPIRED)).toBe( - true, - ); - }); - - it('should allow ACTIVE -> EXPIRED', () => { - expect(canTransition(EscrowStatus.ACTIVE, EscrowStatus.EXPIRED)).toBe( - true, - ); - }); - - it('should allow DISPUTED -> EXPIRED', () => { - expect(canTransition(EscrowStatus.DISPUTED, EscrowStatus.EXPIRED)).toBe( - true, - ); - }); - - it('should not allow transitions from EXPIRED', () => { - expect(canTransition(EscrowStatus.EXPIRED, EscrowStatus.PENDING)).toBe( - false, - ); - expect(canTransition(EscrowStatus.EXPIRED, EscrowStatus.ACTIVE)).toBe( - false, - ); - expect(canTransition(EscrowStatus.EXPIRED, EscrowStatus.COMPLETED)).toBe( - false, - ); - }); - - it('should return false for invalid current status', () => { - expect(canTransition('INVALID' as any, EscrowStatus.ACTIVE)).toBe(false); - }); - }); - - describe('validateTransition', () => { - it('should not throw for valid transitions', () => { - expect(() => - validateTransition(EscrowStatus.PENDING, EscrowStatus.ACTIVE), - ).not.toThrow(); - }); - - it('should throw BadRequestException for invalid transitions', () => { - expect(() => - validateTransition(EscrowStatus.PENDING, EscrowStatus.COMPLETED), - ).toThrow(BadRequestException); - }); - - it('should include status names in error message', () => { - expect(() => - validateTransition(EscrowStatus.COMPLETED, EscrowStatus.ACTIVE), - ).toThrow("Invalid status transition from 'completed' to 'active'"); - }); - }); - - describe('isTerminalStatus', () => { - it('should return true for COMPLETED', () => { - expect(isTerminalStatus(EscrowStatus.COMPLETED)).toBe(true); - }); - - it('should return true for CANCELLED', () => { - expect(isTerminalStatus(EscrowStatus.CANCELLED)).toBe(true); - }); - - it('should return true for EXPIRED', () => { - expect(isTerminalStatus(EscrowStatus.EXPIRED)).toBe(true); - }); - - it('should return false for PENDING', () => { - expect(isTerminalStatus(EscrowStatus.PENDING)).toBe(false); - }); - - it('should return false for ACTIVE', () => { - expect(isTerminalStatus(EscrowStatus.ACTIVE)).toBe(false); - }); - - it('should return false for DISPUTED', () => { - expect(isTerminalStatus(EscrowStatus.DISPUTED)).toBe(false); - }); - }); -}); diff --git a/apps/backend/src/modules/escrow/escrow-state-machine.ts b/apps/backend/src/modules/escrow/escrow-state-machine.ts deleted file mode 100644 index 1a260eef..00000000 --- a/apps/backend/src/modules/escrow/escrow-state-machine.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { BadRequestException } from '@nestjs/common'; -import { EscrowStatus } from './entities/escrow.entity'; - -const validTransitions: Record = { - [EscrowStatus.PENDING]: [ - EscrowStatus.ACTIVE, - EscrowStatus.CANCELLED, - EscrowStatus.EXPIRED, - ], - [EscrowStatus.ACTIVE]: [ - EscrowStatus.COMPLETED, - EscrowStatus.CANCELLED, - EscrowStatus.DISPUTED, - EscrowStatus.EXPIRED, - ], - [EscrowStatus.DISPUTED]: [ - EscrowStatus.COMPLETED, - EscrowStatus.CANCELLED, - EscrowStatus.EXPIRED, - ], - [EscrowStatus.COMPLETED]: [], - [EscrowStatus.CANCELLED]: [], - [EscrowStatus.EXPIRED]: [], -}; - -export function canTransition( - currentStatus: EscrowStatus, - newStatus: EscrowStatus, -): boolean { - return validTransitions[currentStatus]?.includes(newStatus) ?? false; -} - -export function validateTransition( - currentStatus: EscrowStatus, - newStatus: EscrowStatus, -): void { - if (!canTransition(currentStatus, newStatus)) { - throw new BadRequestException( - `Invalid status transition from '${currentStatus}' to '${newStatus}'`, - ); - } -} - -export function isTerminalStatus(status: EscrowStatus): boolean { - return ( - status === EscrowStatus.COMPLETED || - status === EscrowStatus.CANCELLED || - status === EscrowStatus.EXPIRED - ); -} diff --git a/apps/backend/src/modules/escrow/escrow-timeout.service.ts b/apps/backend/src/modules/escrow/escrow-timeout.service.ts deleted file mode 100644 index 6d83076c..00000000 --- a/apps/backend/src/modules/escrow/escrow-timeout.service.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { - Dispute, - DisputeOutcome, - DisputeStatus, -} from './entities/dispute.entity'; - -@Injectable() -export class EscrowTimeoutService { - private readonly logger = new Logger(EscrowTimeoutService.name); - - constructor( - @InjectRepository(Dispute) - private readonly disputeRepo: Repository, - ) {} - - /** - * Auto-resolves disputes that have been OPEN longer than `timeoutHours`. - * Defaults the outcome to REFUNDED_TO_BUYER when no admin action is taken. - */ - async resolveExpiredDisputes(timeoutHours = 72): Promise { - const cutoff = new Date(Date.now() - timeoutHours * 60 * 60 * 1000); - const openDisputes = await this.disputeRepo.find({ - where: { status: DisputeStatus.OPEN }, - }); - - const expired = openDisputes.filter((d) => d.createdAt < cutoff); - - for (const dispute of expired) { - dispute.status = DisputeStatus.RESOLVED; - dispute.outcome = DisputeOutcome.REFUNDED_TO_BUYER; - dispute.resolvedAt = new Date(); - dispute.resolutionNotes = `Auto-resolved: no admin action within ${timeoutHours}h`; - await this.disputeRepo.save(dispute); - this.logger.log( - `Dispute ${dispute.id} auto-resolved after ${timeoutHours}h timeout`, - ); - } - - return expired.length; - } -} diff --git a/apps/backend/src/modules/escrow/escrow.module.ts b/apps/backend/src/modules/escrow/escrow.module.ts deleted file mode 100644 index d5f9f0e6..00000000 --- a/apps/backend/src/modules/escrow/escrow.module.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { Escrow } from './entities/escrow.entity'; -import { Party } from './entities/party.entity'; -import { Condition } from './entities/condition.entity'; -import { EscrowEvent } from './entities/escrow-event.entity'; -import { Dispute } from './entities/dispute.entity'; -import { EscrowService } from './services/escrow.service'; -import { EscrowSchedulerService } from './services/escrow-scheduler.service'; -import { EscrowController } from './controllers/escrow.controller'; -import { EscrowSchedulerController } from './controllers/escrow-scheduler.controller'; -import { EventsController } from './controllers/events.controller'; -import { EscrowAccessGuard } from './guards/escrow-access.guard'; -import { EscrowExpireGuard } from './guards/escrow-expire.guard'; -import { AuthModule } from '../auth/auth.module'; -import { EscrowStellarIntegrationService } from './services/escrow-stellar-integration.service'; -import { WebhookModule } from '../webhook/webhook.module'; -import { IpfsModule } from '../ipfs/ipfs.module'; -import { NotificationsModule } from '../../notifications/notifications.module'; -import { User } from '../user/entities/user.entity'; -import { AllowedAsset } from '../assets/entities/allowed-asset.entity'; -import { EscrowLifecycleService } from './escrow-lifecycle.service'; -import { EscrowFundingService } from './escrow-funding.service'; -import { EscrowDisputeService } from './escrow-dispute.service'; -import { EscrowQueryService } from './escrow-query.service'; -import { EscrowEvidenceService } from './services/escrow-evidence.service'; -import { EscrowIpfsSyncService } from './services/escrow-ipfs-sync.service'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([ - Escrow, - Party, - Condition, - EscrowEvent, - Dispute, - User, - AllowedAsset, - ]), - AuthModule, - WebhookModule, - IpfsModule, - NotificationsModule, - ], - controllers: [EscrowController, EscrowSchedulerController, EventsController], - providers: [ - EscrowService, - EscrowSchedulerService, - EscrowStellarIntegrationService, - EscrowAccessGuard, - EscrowExpireGuard, - EscrowLifecycleService, - EscrowFundingService, - EscrowDisputeService, - EscrowQueryService, - EscrowEvidenceService, - EscrowIpfsSyncService, - ], - exports: [ - EscrowService, - EscrowSchedulerService, - EscrowLifecycleService, - EscrowFundingService, - EscrowDisputeService, - EscrowQueryService, - EscrowEvidenceService, - EscrowIpfsSyncService, - ], -}) -export class EscrowModule {} diff --git a/apps/backend/src/modules/escrow/guards/escrow-access.guard.ts b/apps/backend/src/modules/escrow/guards/escrow-access.guard.ts deleted file mode 100644 index a3b7c3a5..00000000 --- a/apps/backend/src/modules/escrow/guards/escrow-access.guard.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - Injectable, - CanActivate, - ExecutionContext, - ForbiddenException, - NotFoundException, -} from '@nestjs/common'; -import { Request } from 'express'; -import { EscrowService } from '../services/escrow.service'; -import { Escrow } from '../entities/escrow.entity'; - -interface AuthUser { - userId: string; - walletAddress: string; -} - -interface AuthenticatedRequest extends Request { - user?: AuthUser; - params: { id?: string }; - escrow?: Escrow; -} - -@Injectable() -export class EscrowAccessGuard implements CanActivate { - constructor(private escrowService: EscrowService) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - const user = request.user; - const escrowId = request.params.id; - - if (!user || !user.userId) { - throw new ForbiddenException('User not authenticated'); - } - - if (!escrowId) { - return true; - } - - const escrow = await this.escrowService.findOne(escrowId); - if (!escrow) { - throw new NotFoundException('Escrow not found'); - } - - const isParty = await this.escrowService.isUserPartyToEscrow( - escrowId, - user.userId, - ); - - if (!isParty) { - throw new ForbiddenException('You do not have access to this escrow'); - } - - request.escrow = escrow; - return true; - } -} diff --git a/apps/backend/src/modules/escrow/guards/escrow-expire.guard.ts b/apps/backend/src/modules/escrow/guards/escrow-expire.guard.ts deleted file mode 100644 index 236d03fc..00000000 --- a/apps/backend/src/modules/escrow/guards/escrow-expire.guard.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - CanActivate, - ExecutionContext, - ForbiddenException, - Injectable, - NotFoundException, -} from '@nestjs/common'; -import { Request } from 'express'; -import { Escrow } from '../entities/escrow.entity'; -import { EscrowService } from '../services/escrow.service'; - -interface AuthUser { - sub?: string; - userId?: string; -} - -interface AuthenticatedRequest extends Request { - user?: AuthUser; - params: { id?: string }; - escrow?: Escrow; -} - -@Injectable() -export class EscrowExpireGuard implements CanActivate { - constructor(private readonly escrowService: EscrowService) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - const actorId = request.user?.sub ?? request.user?.userId; - const escrowId = request.params.id; - - if (!actorId) { - throw new ForbiddenException('User not authenticated'); - } - - if (!escrowId) { - return true; - } - - const escrow = await this.escrowService.findOne(escrowId); - if (!escrow) { - throw new NotFoundException('Escrow not found'); - } - - request.escrow = escrow; - - if (await this.escrowService.isUserAdmin(actorId)) { - return true; - } - - const isParty = await this.escrowService.isUserPartyToEscrow( - escrowId, - actorId, - ); - - if (!isParty) { - throw new ForbiddenException('You do not have access to this escrow'); - } - - return true; - } -} diff --git a/apps/backend/src/modules/escrow/services/cursor-monotonicity.spec.ts b/apps/backend/src/modules/escrow/services/cursor-monotonicity.spec.ts deleted file mode 100644 index f1f13b98..00000000 --- a/apps/backend/src/modules/escrow/services/cursor-monotonicity.spec.ts +++ /dev/null @@ -1,355 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; - -import { EscrowEvent, EscrowEventType } from '../entities/escrow-event.entity'; -import { EscrowService } from './escrow.service'; -import { EscrowStellarIntegrationService } from './escrow-stellar-integration.service'; -import { Escrow } from '../entities/escrow.entity'; -import { User } from '../../user/entities/user.entity'; -import { Party } from '../entities/party.entity'; -import { Condition } from '../entities/condition.entity'; -import { Dispute } from '../entities/dispute.entity'; -import { AllowedAsset } from '../../assets/entities/allowed-asset.entity'; -import { StellarService } from '../../../services/stellar.service'; -import { WebhookService } from '../../../services/webhook/webhook.service'; -import { IpfsService } from '../../ipfs/ipfs.service'; - -describe.skip('Cursor Monotonicity Tests', () => { - let service: EscrowService; - let eventRepository: jest.Mocked>; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - EscrowService, - { - provide: getRepositoryToken(Escrow), - useValue: { - findOne: jest.fn(), - find: jest.fn(), - create: jest.fn(), - save: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - }, - }, - { - provide: getRepositoryToken(Party), - useValue: { - findOne: jest.fn(), - find: jest.fn(), - create: jest.fn(), - save: jest.fn(), - }, - }, - { - provide: getRepositoryToken(Condition), - useValue: { - findOne: jest.fn(), - find: jest.fn(), - create: jest.fn(), - save: jest.fn(), - }, - }, - { - provide: getRepositoryToken(EscrowEvent), - useValue: { - findOne: jest.fn(), - find: jest.fn(), - create: jest.fn(), - save: jest.fn(), - count: jest.fn(), - }, - }, - { - provide: getRepositoryToken(Dispute), - useValue: { - findOne: jest.fn(), - find: jest.fn(), - create: jest.fn(), - save: jest.fn(), - }, - }, - { - provide: getRepositoryToken(User), - useValue: { - findOne: jest.fn(), - find: jest.fn(), - create: jest.fn(), - save: jest.fn(), - }, - }, - { - provide: getRepositoryToken(AllowedAsset), - useValue: { - findOne: jest.fn(), - find: jest.fn(), - }, - }, - { - provide: EscrowStellarIntegrationService, - useValue: { - createOnChainEscrow: jest.fn(), - fundOnChainEscrow: jest.fn(), - releaseOnChainEscrow: jest.fn(), - cancelOnChainEscrow: jest.fn(), - }, - }, - { - provide: WebhookService, - useValue: { - sendNotification: jest.fn(), - }, - }, - { - provide: IpfsService, - useValue: { - uploadFile: jest.fn(), - getGatewayUrl: jest.fn(), - }, - }, - { - provide: 'EscrowLifecycleService', - useValue: { - validateTransition: jest.fn(), - }, - }, - { - provide: 'EscrowFundingService', - useValue: { - fundEscrow: jest.fn(), - }, - }, - { - provide: 'EscrowDisputeService', - useValue: { - fileDispute: jest.fn(), - resolveDispute: jest.fn(), - }, - }, - { - provide: 'EscrowQueryService', - useValue: { - findOne: jest.fn(), - find: jest.fn(), - }, - }, - { - provide: StellarService, - useValue: { - getRpc: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(EscrowService); - eventRepository = module.get(getRepositoryToken(EscrowEvent)); - }); - - describe('EscrowEvent Cursor Monotonicity', () => { - it('should assign monotonic cursor values to events', () => { - const mockEvents: EscrowEvent[] = [ - { - id: '1', - escrowId: 'escrow-1', - eventType: EscrowEventType.CREATED, - cursor: '1', - createdAt: new Date(), - } as EscrowEvent, - { - id: '2', - escrowId: 'escrow-1', - eventType: EscrowEventType.FUNDED, - cursor: '2', - createdAt: new Date(), - } as EscrowEvent, - { - id: '3', - escrowId: 'escrow-1', - eventType: EscrowEventType.COMPLETED, - cursor: '3', - createdAt: new Date(), - } as EscrowEvent, - ]; - - // Simulate sequential event creation - (eventRepository.findOne as jest.Mock) - .mockResolvedValueOnce(null) // First event - no previous cursor - .mockResolvedValueOnce(mockEvents[0]) // Second event - previous cursor is 1 - .mockResolvedValueOnce(mockEvents[1]); // Third event - previous cursor is 2 - - (eventRepository.create as jest.Mock) - .mockReturnValueOnce(mockEvents[0]) - .mockReturnValueOnce(mockEvents[1]) - .mockReturnValueOnce(mockEvents[2]); - - (eventRepository.save as jest.Mock) - .mockResolvedValueOnce(mockEvents[0]) - .mockResolvedValueOnce(mockEvents[1]) - .mockResolvedValueOnce(mockEvents[2]); - - // Verify cursor values are monotonic - const cursors = mockEvents.map((e) => BigInt(e.cursor)); - expect(cursors[0]).toBeLessThan(cursors[1]); - expect(cursors[1]).toBeLessThan(cursors[2]); - expect(cursors[2] - cursors[1]).toBe(BigInt(1)); - expect(cursors[1] - cursors[0]).toBe(BigInt(1)); - }); - - it('should ensure cursor is present in all events', () => { - const mockEvent: EscrowEvent = { - id: '1', - escrowId: 'escrow-1', - eventType: EscrowEventType.CREATED, - cursor: '1', - createdAt: new Date(), - } as EscrowEvent; - - (eventRepository.findOne as jest.Mock).mockResolvedValue(null); - (eventRepository.create as jest.Mock).mockReturnValue(mockEvent); - (eventRepository.save as jest.Mock).mockResolvedValue(mockEvent); - - expect(mockEvent.cursor).toBeDefined(); - expect(mockEvent.cursor).toBe('1'); - }); - - it('should handle cursor increment from last event', () => { - const lastEvent: EscrowEvent = { - id: '1', - escrowId: 'escrow-1', - eventType: EscrowEventType.CREATED, - cursor: '100', - createdAt: new Date(), - } as EscrowEvent; - - const newEvent: EscrowEvent = { - id: '2', - escrowId: 'escrow-1', - eventType: EscrowEventType.FUNDED, - cursor: '101', - createdAt: new Date(), - } as EscrowEvent; - - (eventRepository.findOne as jest.Mock).mockResolvedValue(lastEvent); - (eventRepository.create as jest.Mock).mockReturnValue(newEvent); - (eventRepository.save as jest.Mock).mockResolvedValue(newEvent); - - expect(BigInt(newEvent.cursor)).toBe( - BigInt(lastEvent.cursor) + BigInt(1), - ); - }); - }); - - describe('Cursor-Based Pagination', () => { - it('should support cursor-based pagination with after parameter', () => { - const mockEvents: EscrowEvent[] = [ - { - id: '1', - escrowId: 'escrow-1', - eventType: EscrowEventType.CREATED, - cursor: '10', - createdAt: new Date(), - } as EscrowEvent, - { - id: '2', - escrowId: 'escrow-1', - eventType: EscrowEventType.FUNDED, - cursor: '11', - createdAt: new Date(), - } as EscrowEvent, - ]; - - const query = { - after: '5', - limit: 10, - page: 1, - }; - - // Verify cursor filtering logic - const filteredEvents = mockEvents.filter( - (e) => BigInt(e.cursor) > BigInt(query.after), - ); - - expect(filteredEvents.length).toBe(2); - expect(BigInt(filteredEvents[0].cursor)).toBeGreaterThan( - BigInt(query.after), - ); - expect(BigInt(filteredEvents[1].cursor)).toBeGreaterThan( - BigInt(query.after), - ); - }); - - it('should support cursor-based pagination with before parameter', () => { - const mockEvents: EscrowEvent[] = [ - { - id: '1', - escrowId: 'escrow-1', - eventType: EscrowEventType.CREATED, - cursor: '5', - createdAt: new Date(), - } as EscrowEvent, - { - id: '2', - escrowId: 'escrow-1', - eventType: EscrowEventType.FUNDED, - cursor: '6', - createdAt: new Date(), - } as EscrowEvent, - ]; - - const query = { - before: '10', - limit: 10, - page: 1, - }; - - // Verify cursor filtering logic - const filteredEvents = mockEvents.filter( - (e) => BigInt(e.cursor) < BigInt(query.before), - ); - - expect(filteredEvents.length).toBe(2); - expect(BigInt(filteredEvents[0].cursor)).toBeLessThan( - BigInt(query.before), - ); - expect(BigInt(filteredEvents[1].cursor)).toBeLessThan( - BigInt(query.before), - ); - }); - - it('should return nextCursor and prevCursor for pagination', () => { - const mockEvents: EscrowEvent[] = [ - { - id: '1', - escrowId: 'escrow-1', - eventType: EscrowEventType.CREATED, - cursor: '10', - createdAt: new Date(), - } as EscrowEvent, - { - id: '2', - escrowId: 'escrow-1', - eventType: EscrowEventType.FUNDED, - cursor: '11', - createdAt: new Date(), - } as EscrowEvent, - ]; - - (eventRepository.count as jest.Mock) - .mockResolvedValueOnce(1) // More events after - .mockResolvedValueOnce(0); // No events before - - const lastCursor = mockEvents[mockEvents.length - 1].cursor; - const firstCursor = mockEvents[0].cursor; - - // Simulate nextCursor calculation - const nextCursor = lastCursor; - const prevCursor = undefined; // No events before - - expect(nextCursor).toBe('11'); - expect(prevCursor).toBeUndefined(); - }); - }); -}); diff --git a/apps/backend/src/modules/escrow/services/escrow-evidence.service.spec.ts b/apps/backend/src/modules/escrow/services/escrow-evidence.service.spec.ts deleted file mode 100644 index dc7db584..00000000 --- a/apps/backend/src/modules/escrow/services/escrow-evidence.service.spec.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { - BadRequestException, - ForbiddenException, - NotFoundException, -} from '@nestjs/common'; -import { Repository } from 'typeorm'; -import { Response } from 'express'; -import { EscrowEvidenceService } from './escrow-evidence.service'; -import { IpfsService } from '../../ipfs/ipfs.service'; -import { Dispute } from '../entities/dispute.entity'; -import { Escrow } from '../entities/escrow.entity'; -import { EvidenceFileMetadataDto } from '../dto/upload-evidence.dto'; - -describe('EscrowEvidenceService', () => { - let service: EscrowEvidenceService; - let disputeRepo: jest.Mocked>; - let escrowRepo: jest.Mocked>; - let ipfsService: jest.Mocked; - - const mockEscrowId = 'escrow-123'; - const mockDisputeId = 'dispute-123'; - const mockUserId = 'user-123'; - const mockCid = 'QmAbc123...'; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - EscrowEvidenceService, - { - provide: getRepositoryToken(Dispute), - useValue: { - findOne: jest.fn(), - save: jest.fn(), - }, - }, - { - provide: getRepositoryToken(Escrow), - useValue: { - findOne: jest.fn(), - }, - }, - { - provide: IpfsService, - useValue: { - uploadFile: jest.fn(), - getGatewayUrl: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(EscrowEvidenceService); - disputeRepo = module.get(getRepositoryToken(Dispute)); - escrowRepo = module.get(getRepositoryToken(Escrow)); - ipfsService = module.get(IpfsService); - }); - - describe('uploadEvidence', () => { - it('should upload files to IPFS and persist CIDs to dispute', async () => { - // Mock files - const files = [ - { - buffer: Buffer.from('pdf content'), - originalname: 'document.pdf', - mimetype: 'application/pdf', - size: 1024, - } as Express.Multer.File, - { - buffer: Buffer.from('image content'), - originalname: 'screenshot.png', - mimetype: 'image/png', - size: 2048, - } as Express.Multer.File, - ]; - - const mockDispute: Dispute = { - id: mockDisputeId, - escrowId: mockEscrowId, - evidenceFiles: [], - reason: 'Non-delivery', - status: 'open' as any, - createdAt: new Date(), - updatedAt: new Date(), - } as Dispute; - - const mockEscrow: Escrow = { - id: mockEscrowId, - } as Escrow; - - escrowRepo.findOne.mockResolvedValue(mockEscrow); - disputeRepo.findOne.mockResolvedValue(mockDispute); - ipfsService.uploadFile.mockResolvedValueOnce(mockCid); - ipfsService.uploadFile.mockResolvedValueOnce('QmDef456...'); - - const savedDispute = { ...mockDispute }; - disputeRepo.save.mockResolvedValue(savedDispute); - - const result = await service.uploadEvidence( - mockEscrowId, - files, - mockUserId, - ); - - expect(result.escrowId).toBe(mockEscrowId); - expect(result.disputeId).toBe(mockDisputeId); - expect(result.uploadedFiles).toHaveLength(2); - expect(result.uploadedFiles[0].cid).toBe(mockCid); - expect(result.uploadedFiles[0].name).toBe('document.pdf'); - expect(result.uploadedFiles[1].name).toBe('screenshot.png'); - expect(ipfsService.uploadFile).toHaveBeenCalledTimes(2); - expect(disputeRepo.save).toHaveBeenCalled(); - }); - - it('should reject files with invalid MIME types', async () => { - const invalidFile = { - buffer: Buffer.from('executable'), - originalname: 'malware.exe', - mimetype: 'application/x-msdownload', - size: 1024, - } as Express.Multer.File; - - const mockEscrow: Escrow = { id: mockEscrowId } as Escrow; - const mockDispute: Dispute = { - id: mockDisputeId, - escrowId: mockEscrowId, - } as Dispute; - - escrowRepo.findOne.mockResolvedValue(mockEscrow); - disputeRepo.findOne.mockResolvedValue(mockDispute); - - await expect( - service.uploadEvidence(mockEscrowId, [invalidFile], mockUserId), - ).rejects.toThrow(BadRequestException); - }); - - it('should reject files over 10MB', async () => { - const largeFile = { - buffer: Buffer.alloc(11 * 1024 * 1024), - originalname: 'large.pdf', - mimetype: 'application/pdf', - size: 11 * 1024 * 1024, - } as Express.Multer.File; - - const mockEscrow: Escrow = { id: mockEscrowId } as Escrow; - const mockDispute: Dispute = { - id: mockDisputeId, - escrowId: mockEscrowId, - } as Dispute; - - escrowRepo.findOne.mockResolvedValue(mockEscrow); - disputeRepo.findOne.mockResolvedValue(mockDispute); - - await expect( - service.uploadEvidence(mockEscrowId, [largeFile], mockUserId), - ).rejects.toThrow(BadRequestException); - }); - - it('should append evidence without replacing existing files', async () => { - const existingFile: EvidenceFileMetadataDto = { - cid: 'QmExisting', - name: 'existing.pdf', - type: 'application/pdf', - size: 1024, - uploadedAt: new Date().toISOString(), - uploadedBy: 'user-old', - }; - - const newFile = { - buffer: Buffer.from('content'), - originalname: 'new.png', - mimetype: 'image/png', - size: 2048, - } as Express.Multer.File; - - const mockDispute: Dispute = { - id: mockDisputeId, - escrowId: mockEscrowId, - evidenceFiles: [existingFile], - } as Dispute; - - const mockEscrow: Escrow = { id: mockEscrowId } as Escrow; - - escrowRepo.findOne.mockResolvedValue(mockEscrow); - disputeRepo.findOne.mockResolvedValue(mockDispute); - ipfsService.uploadFile.mockResolvedValue('QmNewCid'); - - const updatedDispute = { ...mockDispute }; - disputeRepo.save.mockResolvedValue(updatedDispute); - - const result = await service.uploadEvidence( - mockEscrowId, - [newFile], - mockUserId, - ); - - // Verify that the saved dispute has both old and new files - const savedCall = disputeRepo.save.mock.calls[0][0]; - expect(savedCall.evidenceFiles).toHaveLength(2); - expect(savedCall.evidenceFiles[0]).toEqual(existingFile); - expect(savedCall.evidenceFiles[1].name).toBe('new.png'); - }); - }); - - describe('getEvidence', () => { - it('should return evidence list for dispute parties', async () => { - const mockMetadata: EvidenceFileMetadataDto[] = [ - { - cid: mockCid, - name: 'document.pdf', - type: 'application/pdf', - size: 1024, - uploadedAt: new Date().toISOString(), - uploadedBy: mockUserId, - }, - ]; - - const mockEscrow: Escrow = { - id: mockEscrowId, - parties: [{ userId: mockUserId }] as any, - } as Escrow; - - const mockDispute: Dispute = { - id: mockDisputeId, - escrowId: mockEscrowId, - evidenceFiles: mockMetadata, - } as Dispute; - - escrowRepo.findOne.mockResolvedValue(mockEscrow); - disputeRepo.findOne.mockResolvedValue(mockDispute); - - const result = await service.getEvidence(mockEscrowId, mockUserId); - - expect(result).toEqual(mockMetadata); - }); - - it('should throw 403 for non-party callers', async () => { - const differentUserId = 'user-999'; - - const mockEscrow: Escrow = { - id: mockEscrowId, - parties: [{ userId: mockUserId }] as any, - } as Escrow; - - escrowRepo.findOne.mockResolvedValue(mockEscrow); - - await expect( - service.getEvidence(mockEscrowId, differentUserId), - ).rejects.toThrow(ForbiddenException); - }); - - it('should throw NotFoundException if no dispute exists', async () => { - const mockEscrow: Escrow = { - id: mockEscrowId, - parties: [{ userId: mockUserId }] as any, - } as Escrow; - - escrowRepo.findOne.mockResolvedValue(mockEscrow); - disputeRepo.findOne.mockResolvedValue(null); - - await expect( - service.getEvidence(mockEscrowId, mockUserId), - ).rejects.toThrow(NotFoundException); - }); - }); - - describe('getEvidenceFile', () => { - it('should stream file from IPFS with correct Content-Type', async () => { - const mockMetadata: EvidenceFileMetadataDto = { - cid: mockCid, - name: 'document.pdf', - type: 'application/pdf', - size: 1024, - uploadedAt: new Date().toISOString(), - uploadedBy: mockUserId, - }; - - const mockEscrow: Escrow = { - id: mockEscrowId, - parties: [{ userId: mockUserId }] as any, - } as Escrow; - - const mockDispute: Dispute = { - id: mockDisputeId, - escrowId: mockEscrowId, - evidenceFiles: [mockMetadata], - } as Dispute; - - const mockResponse = { - setHeader: jest.fn(), - redirect: jest.fn(), - } as unknown as Response; - - escrowRepo.findOne.mockResolvedValue(mockEscrow); - disputeRepo.findOne.mockResolvedValue(mockDispute); - ipfsService.getGatewayUrl.mockReturnValue( - 'https://gateway.ipfs.io/ipfs/Qm...', - ); - - await service.getEvidenceFile( - mockEscrowId, - mockCid, - mockUserId, - mockResponse, - ); - - expect(mockResponse.setHeader).toHaveBeenCalledWith( - 'Content-Type', - 'application/pdf', - ); - expect(mockResponse.redirect).toHaveBeenCalledWith( - 302, - 'https://gateway.ipfs.io/ipfs/Qm...', - ); - }); - - it('should throw NotFoundException if CID not found', async () => { - const mockEscrow: Escrow = { - id: mockEscrowId, - parties: [{ userId: mockUserId }] as any, - } as Escrow; - - const mockDispute: Dispute = { - id: mockDisputeId, - escrowId: mockEscrowId, - evidenceFiles: [], - } as Dispute; - - const mockResponse = {} as Response; - - escrowRepo.findOne.mockResolvedValue(mockEscrow); - disputeRepo.findOne.mockResolvedValue(mockDispute); - - await expect( - service.getEvidenceFile( - mockEscrowId, - 'QmNonExistent', - mockUserId, - mockResponse, - ), - ).rejects.toThrow(NotFoundException); - }); - }); -}); diff --git a/apps/backend/src/modules/escrow/services/escrow-evidence.service.ts b/apps/backend/src/modules/escrow/services/escrow-evidence.service.ts deleted file mode 100644 index 351c0743..00000000 --- a/apps/backend/src/modules/escrow/services/escrow-evidence.service.ts +++ /dev/null @@ -1,273 +0,0 @@ -import { - Injectable, - BadRequestException, - NotFoundException, - ForbiddenException, - Logger, -} from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { Response } from 'express'; -import { Dispute } from '../entities/dispute.entity'; -import { Escrow } from '../entities/escrow.entity'; -import { IpfsService } from '../../ipfs/ipfs.service'; -import { - EvidenceFileMetadataDto, - UploadEvidenceResponseDto, -} from '../dto/upload-evidence.dto'; - -interface FileWithMetadata extends Express.Multer.File { - buffer: Buffer; - originalname: string; - mimetype: string; - size: number; -} - -@Injectable() -export class EscrowEvidenceService { - private readonly logger = new Logger(EscrowEvidenceService.name); - - // Allowed MIME types for evidence files - private readonly ALLOWED_MIME_TYPES = [ - 'application/pdf', - 'image/png', - 'image/jpeg', - 'application/msword', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - ]; - - // File size limit: 10MB - private readonly MAX_FILE_SIZE = 10 * 1024 * 1024; - - constructor( - @InjectRepository(Dispute) - private disputeRepo: Repository, - @InjectRepository(Escrow) - private escrowRepo: Repository, - private ipfsService: IpfsService, - ) {} - - /** - * Upload evidence files for a dispute - * - Validates escrow exists and has an open dispute - * - Validates each file (type, size) - * - Uploads each file to IPFS - * - Appends metadata to dispute.evidenceFiles (never replaces) - * - Returns metadata with CIDs - */ - async uploadEvidence( - escrowId: string, - files: FileWithMetadata[], - userId: string, - ): Promise { - this.logger.log( - `Uploading ${files.length} evidence files for escrow ${escrowId} by user ${userId}`, - ); - - // Validate escrow exists - const escrow = await this.escrowRepo.findOne({ - where: { id: escrowId }, - relations: ['dispute'], - }); - - if (!escrow) { - throw new NotFoundException('Escrow not found'); - } - - // Check if escrow has an open dispute - let dispute = await this.disputeRepo.findOne({ - where: { escrowId }, - }); - - if (!dispute) { - throw new BadRequestException('No dispute found for this escrow'); - } - - // Validate each file - for (const file of files) { - this.validateFile(file); - } - - const uploadedMetadata: EvidenceFileMetadataDto[] = []; - - // Upload each file to IPFS and build metadata - for (const file of files) { - try { - this.logger.log(`Uploading file ${file.originalname} to IPFS`); - - const cid = await this.ipfsService.uploadFile( - file.buffer, - file.originalname, - ); - - const metadata: EvidenceFileMetadataDto = { - cid, - name: file.originalname, - type: file.mimetype, - size: file.size, - uploadedAt: new Date().toISOString(), - uploadedBy: userId, - }; - - uploadedMetadata.push(metadata); - } catch (error) { - this.logger.error( - `Failed to upload file ${file.originalname} to IPFS`, - error, - ); - throw new BadRequestException( - `Failed to upload file ${file.originalname}`, - ); - } - } - - // Append to existing evidence files (do NOT replace) - if (!dispute.evidenceFiles) { - dispute.evidenceFiles = []; - } - - dispute.evidenceFiles = [...dispute.evidenceFiles, ...uploadedMetadata]; - - // Save the dispute record - dispute = await this.disputeRepo.save(dispute); - - this.logger.log( - `Successfully uploaded ${uploadedMetadata.length} files for dispute ${dispute.id}`, - ); - - return { - escrowId, - disputeId: dispute.id, - uploadedFiles: uploadedMetadata, - message: `Successfully uploaded ${uploadedMetadata.length} evidence file(s)`, - }; - } - - /** - * Get all evidence files for a dispute - * - Verify caller is buyer, seller, or admin - * - Return metadata for all evidence files - */ - async getEvidence( - escrowId: string, - userId: string, - ): Promise { - this.logger.log( - `Fetching evidence for escrow ${escrowId} by user ${userId}`, - ); - - // Verify access - await this.verifyEscrowAccess(escrowId, userId); - - // Get dispute - const dispute = await this.disputeRepo.findOne({ - where: { escrowId }, - }); - - if (!dispute) { - throw new NotFoundException('Dispute not found for this escrow'); - } - - return dispute.evidenceFiles || []; - } - - /** - * Stream evidence file from IPFS - * - Verify caller has access to escrow - * - Find evidence file metadata by CID - * - Fetch file from IPFS - * - Stream with correct Content-Type header - */ - async getEvidenceFile( - escrowId: string, - cid: string, - userId: string, - response: Response, - ): Promise { - this.logger.log( - `Fetching evidence file ${cid} for escrow ${escrowId} by user ${userId}`, - ); - - // Verify access - await this.verifyEscrowAccess(escrowId, userId); - - // Get dispute - const dispute = await this.disputeRepo.findOne({ - where: { escrowId }, - }); - - if (!dispute) { - throw new NotFoundException('Dispute not found for this escrow'); - } - - // Find evidence file metadata by CID - const fileMetadata = (dispute.evidenceFiles || []).find( - (file) => file.cid === cid, - ); - - if (!fileMetadata) { - throw new NotFoundException('Evidence file not found'); - } - - // Get gateway URL from IPFS service - const gatewayUrl = this.ipfsService.getGatewayUrl(cid); - - this.logger.log(`Redirecting to IPFS gateway: ${gatewayUrl}`); - - // Set Content-Type header from stored file metadata - response.setHeader('Content-Type', fileMetadata.type); - response.setHeader( - 'Content-Disposition', - `inline; filename="${fileMetadata.name}"`, - ); - - // Redirect to IPFS gateway - response.redirect(302, gatewayUrl); - } - - /** - * Validate file before upload - * - Check MIME type against allowlist - * - Check file size (max 10MB) - */ - private validateFile(file: FileWithMetadata): void { - // Validate MIME type - if (!this.ALLOWED_MIME_TYPES.includes(file.mimetype)) { - throw new BadRequestException( - `File type ${file.mimetype} not allowed. Allowed types: PDF, PNG, JPG, JPEG, DOC, DOCX`, - ); - } - - // Validate file size - if (file.size > this.MAX_FILE_SIZE) { - throw new BadRequestException( - `File size exceeds 10MB limit. File size: ${(file.size / 1024 / 1024).toFixed(2)}MB`, - ); - } - } - - /** - * Verify that the user has access to the escrow - * (i.e., they are a party or admin) - */ - private async verifyEscrowAccess( - escrowId: string, - userId: string, - ): Promise { - const escrow = await this.escrowRepo.findOne({ - where: { id: escrowId }, - relations: ['parties'], - }); - - if (!escrow) { - throw new NotFoundException('Escrow not found'); - } - - // Check if user is a party to the escrow - const isParty = escrow.parties?.some((party) => party.userId === userId); - - if (!isParty) { - throw new ForbiddenException('You do not have access to this escrow'); - } - } -} diff --git a/apps/backend/src/modules/escrow/services/escrow-ipfs-sync.service.ts b/apps/backend/src/modules/escrow/services/escrow-ipfs-sync.service.ts deleted file mode 100644 index aa76960d..00000000 --- a/apps/backend/src/modules/escrow/services/escrow-ipfs-sync.service.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, In } from 'typeorm'; -import { Escrow } from '../entities/escrow.entity'; -import { EscrowEvent, EscrowEventType } from '../entities/escrow-event.entity'; -import { IpfsService } from '../../ipfs/ipfs.service'; - -/** - * Service that automatically pins escrow metadata to IPFS at key lifecycle events - */ -@Injectable() -export class EscrowIpfsSyncService implements OnModuleInit { - private readonly logger = new Logger(EscrowIpfsSyncService.name); - private eventListenerInitialized = false; - - constructor( - @InjectRepository(Escrow) - private escrowRepository: Repository, - @InjectRepository(EscrowEvent) - private eventRepository: Repository, - private readonly ipfsService: IpfsService, - ) {} - - onModuleInit() { - if (!this.eventListenerInitialized) { - this.eventListenerInitialized = true; - this.logger.log('Escrow IPFS Sync Service initialized'); - // Start polling for new events every 2 seconds - void this.startEventPolling(); - } - } - - /** - * Poll for new escrow events and trigger IPFS pinning - * This runs in the background to avoid blocking main operations - */ - private startEventPolling() { - setInterval(() => { - void this.processPendingEvents(); - }, 2000); - } - - /** - * Process events that need IPFS metadata pinning - */ - private async processPendingEvents() { - // Find recent events that might need IPFS sync - const recentEvents = await this.eventRepository.find({ - where: { - eventType: In([ - EscrowEventType.CREATED, - EscrowEventType.FUNDED, - EscrowEventType.COMPLETED, - EscrowEventType.DISPUTED, - ]), - }, - order: { createdAt: 'DESC' }, - take: 10, - }); - - for (const event of recentEvents) { - await this.syncMetadataForEvent(event); - } - } - - /** - * Sync metadata to IPFS for specific event types - */ - private async syncMetadataForEvent(event: EscrowEvent) { - const shouldSync = [ - EscrowEventType.CREATED, - EscrowEventType.FUNDED, - EscrowEventType.COMPLETED, - EscrowEventType.DISPUTED, - ].includes(event.eventType); - - if (!shouldSync) { - return; - } - - try { - const escrow = await this.escrowRepository.findOne({ - where: { id: event.escrowId }, - relations: ['parties', 'conditions'], - }); - - if (!escrow) { - return; - } - - // Check if we need to pin (if escrow doesn't have IPFS data) - const needsPinning = !escrow.ipfsCid; - - if (needsPinning) { - this.logger.log( - `Pinning metadata for escrow ${escrow.id} after ${event.eventType} event`, - ); - - // Pin metadata (this will update escrow.ipfsCid, ipfsMetadataHash, ipfsVersion) - await this.ipfsService.pinMetadata(escrow.id, { - status: escrow.status, - }); - } - } catch (error) { - // Graceful failure - log but don't block - this.logger.error( - `Failed to sync IPFS metadata for escrow ${event.escrowId}`, - error, - ); - } - } - - /** - * Manually trigger IPFS metadata pinning for an escrow - * Called by the admin endpoint - */ - async triggerMetadataPin( - escrowId: string, - metadata?: Record, - ) { - return this.ipfsService.pinMetadata(escrowId, metadata || {}); - } -} diff --git a/apps/backend/src/modules/escrow/services/escrow-scheduler.service.spec.ts b/apps/backend/src/modules/escrow/services/escrow-scheduler.service.spec.ts deleted file mode 100644 index 2f3397f6..00000000 --- a/apps/backend/src/modules/escrow/services/escrow-scheduler.service.spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { EscrowSchedulerService } from './escrow-scheduler.service'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { Escrow, EscrowStatus } from '../entities/escrow.entity'; -import { EscrowEvent } from '../entities/escrow-event.entity'; -import { EscrowService } from './escrow.service'; -import { Repository } from 'typeorm'; - -describe('EscrowSchedulerService', () => { - let service: EscrowSchedulerService; - let escrowRepo: jest.Mocked>; - let eventRepo: jest.Mocked>; - let escrowService: jest.Mocked; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - EscrowSchedulerService, - { - provide: getRepositoryToken(Escrow), - useValue: { - find: jest.fn(), - findOne: jest.fn(), - save: jest.fn(), - }, - }, - { - provide: getRepositoryToken(EscrowEvent), - useValue: { - findOne: jest.fn(), - save: jest.fn(), - }, - }, - { - provide: EscrowService, - useValue: { - expireBySystem: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(EscrowSchedulerService); - escrowRepo = module.get(getRepositoryToken(Escrow)); - eventRepo = module.get(getRepositoryToken(EscrowEvent)); - escrowService = module.get(EscrowService); - }); - - const mockEscrow = { - id: 'e1', - status: EscrowStatus.PENDING, - expiresAt: new Date(Date.now() - 1000), - isActive: true, - title: 'Test Escrow', - parties: [{ user: { walletAddress: 'addr1' } }], - }; - - describe('handleExpiredEscrows', () => { - it('should process expired pending and active escrows', async () => { - escrowRepo.find.mockResolvedValueOnce([mockEscrow] as any); // pending - escrowRepo.find.mockResolvedValueOnce([ - { ...mockEscrow, status: EscrowStatus.ACTIVE }, - ] as any); // active - escrowService.expireBySystem.mockResolvedValue({ - ...mockEscrow, - status: EscrowStatus.EXPIRED, - } as any); - - await service.handleExpiredEscrows(); - - expect(escrowService.expireBySystem).toHaveBeenCalledTimes(2); - }); - }); - - describe('sendExpirationWarnings', () => { - it('should process escrows needing warnings', async () => { - escrowRepo.find.mockResolvedValue([ - { - ...mockEscrow, - status: EscrowStatus.ACTIVE, - expiresAt: new Date(Date.now() + 1000 * 60 * 60), - }, - ] as any); - - await service.sendExpirationWarnings(); - - expect(escrowRepo.save).toHaveBeenCalled(); - expect(eventRepo.save).toHaveBeenCalled(); - }); - }); - - describe('processEscrowManually', () => { - it('should throw if escrow not found', async () => { - escrowRepo.findOne.mockResolvedValue(null); - await expect(service.processEscrowManually('e1')).rejects.toThrow( - 'Escrow not found', - ); - }); - - it('should throw if not expired', async () => { - escrowRepo.findOne.mockResolvedValue({ - ...mockEscrow, - expiresAt: new Date(Date.now() + 100000), - } as any); - await expect(service.processEscrowManually('e1')).rejects.toThrow( - 'has not expired yet', - ); - }); - - it('should auto-cancel if pending', async () => { - escrowRepo.findOne.mockResolvedValue(mockEscrow as any); - escrowService.expireBySystem.mockResolvedValue(mockEscrow as any); - await service.processEscrowManually('e1'); - expect(escrowService.expireBySystem).toHaveBeenCalledWith( - 'e1', - 'EXPIRED_PENDING', - ); - }); - }); -}); diff --git a/apps/backend/src/modules/escrow/services/escrow-scheduler.service.ts b/apps/backend/src/modules/escrow/services/escrow-scheduler.service.ts deleted file mode 100644 index 074c2241..00000000 --- a/apps/backend/src/modules/escrow/services/escrow-scheduler.service.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, LessThan, In, IsNull } from 'typeorm'; -import { Cron, CronExpression } from '@nestjs/schedule'; -import { Escrow, EscrowStatus } from '../entities/escrow.entity'; -import { EscrowEvent, EscrowEventType } from '../entities/escrow-event.entity'; -import { EscrowService } from './escrow.service'; - -@Injectable() -export class EscrowSchedulerService { - private readonly logger = new Logger(EscrowSchedulerService.name); - - constructor( - @InjectRepository(Escrow) - private escrowRepository: Repository, - @InjectRepository(EscrowEvent) - private escrowEventRepository: Repository, - private escrowService: EscrowService, - ) {} - - @Cron(CronExpression.EVERY_HOUR) - async handleExpiredEscrows() { - this.logger.log('Starting expired escrow processing...'); - - try { - await this.processExpiredPendingEscrows(); - await this.processExpiredActiveEscrows(); - - this.logger.log('Completed expired escrow processing'); - } catch (error) { - this.logger.error('Error processing expired escrows:', error); - } - } - - @Cron(CronExpression.EVERY_DAY_AT_9AM) - async sendExpirationWarnings() { - this.logger.log('Sending 24-hour expiration warnings...'); - - try { - await this.processExpirationWarnings(); - this.logger.log('Completed expiration warnings'); - } catch (error) { - this.logger.error('Error sending expiration warnings:', error); - } - } - - private async processExpiredPendingEscrows() { - const now = new Date(); - - const expiredPendingEscrows = await this.escrowRepository.find({ - where: { - status: EscrowStatus.PENDING, - expiresAt: LessThan(now), - isActive: true, - }, - relations: ['creator', 'parties', 'parties.user'], - }); - - this.logger.log( - `Found ${expiredPendingEscrows.length} expired pending escrows`, - ); - - for (const escrow of expiredPendingEscrows) { - try { - await this.autoCancelEscrow(escrow); - } catch (error) { - this.logger.error(`Failed to auto-cancel escrow ${escrow.id}:`, error); - } - } - } - - private async processExpiredActiveEscrows() { - const now = new Date(); - - const expiredActiveEscrows = await this.escrowRepository.find({ - where: { - status: EscrowStatus.ACTIVE, - expiresAt: LessThan(now), - isActive: true, - }, - relations: ['creator', 'parties', 'parties.user'], - }); - - this.logger.log( - `Found ${expiredActiveEscrows.length} expired active escrows`, - ); - - for (const escrow of expiredActiveEscrows) { - try { - await this.escalateToDispute(escrow); - } catch (error) { - this.logger.error( - `Failed to escalate escrow ${escrow.id} to dispute:`, - error, - ); - } - } - } - - private async processExpirationWarnings() { - const tomorrow = new Date(); - tomorrow.setDate(tomorrow.getDate() + 1); - - const warningThreshold = new Date(); - warningThreshold.setDate(warningThreshold.getDate() + 1); - warningThreshold.setHours(0, 0, 0, 0); - - const escrowsNeedingWarning = await this.escrowRepository.find({ - where: { - status: In([EscrowStatus.PENDING, EscrowStatus.ACTIVE]), - expiresAt: LessThan(warningThreshold), - expirationNotifiedAt: IsNull(), - isActive: true, - }, - relations: ['creator', 'parties', 'parties.user'], - }); - - this.logger.log( - `Found ${escrowsNeedingWarning.length} escrows needing expiration warnings`, - ); - - for (const escrow of escrowsNeedingWarning) { - try { - await this.sendExpirationWarning(escrow); - } catch (error) { - this.logger.error( - `Failed to send warning for escrow ${escrow.id}:`, - error, - ); - } - } - } - - private async autoCancelEscrow(escrow: Escrow) { - this.logger.log(`Auto-expiring pending escrow: ${escrow.id}`); - - const expiredEscrow = await this.escrowService.expireBySystem( - escrow.id, - 'EXPIRED_PENDING', - ); - - void this.notifyParties(expiredEscrow, 'ESCROW_EXPIRED', { - reason: 'Escrow expired while pending', - expiredAt: expiredEscrow.expiresAt, - }); - - this.logger.log(`Successfully expired pending escrow: ${escrow.id}`); - } - - private async escalateToDispute(escrow: Escrow) { - this.logger.log( - `Escalating expired active escrow to expired status: ${escrow.id}`, - ); - - const expiredEscrow = await this.escrowService.expireBySystem( - escrow.id, - 'EXPIRED_ACTIVE', - ); - - void this.notifyParties(expiredEscrow, 'ESCROW_EXPIRED', { - reason: 'Escrow expired while active', - expiredAt: expiredEscrow.expiresAt, - requiresArbitration: true, - }); - - this.logger.log(`Successfully expired active escrow: ${escrow.id}`); - } - - private async sendExpirationWarning(escrow: Escrow) { - this.logger.log(`Sending expiration warning for escrow: ${escrow.id}`); - - escrow.expirationNotifiedAt = new Date(); - await this.escrowRepository.save(escrow); - - // Get the last cursor value and increment it for monotonic sequence - const lastEvent = await this.escrowEventRepository.findOne({ - where: {}, - order: { cursor: 'DESC' }, - }); - - const lastCursor = lastEvent?.cursor ? BigInt(lastEvent.cursor) : BigInt(0); - const nextCursor = (lastCursor + BigInt(1)).toString(); - - await this.escrowEventRepository.save({ - escrowId: escrow.id, - eventType: EscrowEventType.EXPIRATION_WARNING_SENT, - data: { - expiresAt: escrow.expiresAt, - warnedAt: new Date(), - }, - cursor: nextCursor, - }); - - void this.notifyParties(escrow, 'ESCROW_EXPIRING_SOON', { - expiresAt: escrow.expiresAt, - hoursUntilExpiry: this.getHoursUntilExpiry(escrow.expiresAt!), - }); - - this.logger.log( - `Successfully sent expiration warning for escrow: ${escrow.id}`, - ); - } - - private notifyParties( - escrow: Escrow, - eventType: string, - data: Record, - ) { - const notifications = escrow.parties.map((party) => ({ - walletAddress: party.user.walletAddress, - type: eventType, - data: { - escrowId: escrow.id, - escrowTitle: escrow.title, - ...data, - }, - })); - - this.logger.log( - `Sending ${notifications.length} notifications for escrow ${escrow.id}`, - ); - - for (const notification of notifications) { - try { - void this.sendWebhookNotification(notification); - } catch (error) { - this.logger.error( - `Failed to send notification to ${notification.walletAddress}:`, - error, - ); - } - } - } - - private sendWebhookNotification(notification: Record) { - this.logger.log( - `Sending webhook notification: ${JSON.stringify(notification)}`, - ); - } - - private getHoursUntilExpiry(expiresAt: Date): number { - const now = new Date(); - const diffMs = expiresAt.getTime() - now.getTime(); - return Math.max(0, Math.floor(diffMs / (1000 * 60 * 60))); - } - - async processEscrowManually(escrowId: string): Promise { - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - relations: ['creator', 'parties', 'parties.user'], - }); - - if (!escrow) { - throw new Error(`Escrow not found: ${escrowId}`); - } - - if (!escrow.expiresAt) { - throw new Error(`Escrow ${escrowId} has no expiration date`); - } - - const now = new Date(); - if (escrow.expiresAt > now) { - throw new Error(`Escrow ${escrowId} has not expired yet`); - } - - if (escrow.status === EscrowStatus.PENDING) { - await this.autoCancelEscrow(escrow); - } else if (escrow.status === EscrowStatus.ACTIVE) { - await this.escalateToDispute(escrow); - } else { - this.logger.log( - `Escrow ${escrowId} already in terminal state: ${escrow.status}`, - ); - } - } -} diff --git a/apps/backend/src/modules/escrow/services/escrow-stellar-integration.service.spec.ts b/apps/backend/src/modules/escrow/services/escrow-stellar-integration.service.spec.ts deleted file mode 100644 index 18e84392..00000000 --- a/apps/backend/src/modules/escrow/services/escrow-stellar-integration.service.spec.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { EscrowStellarIntegrationService } from './escrow-stellar-integration.service'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { Escrow } from '../entities/escrow.entity'; -import { Party } from '../entities/party.entity'; -import { Condition } from '../entities/condition.entity'; -import { StellarService } from '../../../services/stellar.service'; -import { EscrowOperationsService } from '../../../services/stellar/escrow-operations'; -import stellarConfig from '../../../config/stellar.config'; - -describe('EscrowStellarIntegrationService', () => { - let service: EscrowStellarIntegrationService; - let escrowRepo: jest.Mocked; - let stellarService: jest.Mocked; - let escrowOps: jest.Mocked; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - EscrowStellarIntegrationService, - { - provide: stellarConfig.KEY, - useValue: { network: 'testnet' }, - }, - { - provide: StellarService, - useValue: { - buildTransaction: jest.fn().mockResolvedValue({}), - submitTransaction: jest.fn().mockResolvedValue({ hash: 'tx-hash' }), - streamTransactions: jest.fn(), - getAccount: jest.fn().mockResolvedValue({ - balances: [{ asset_type: 'native', balance: '1000' }], - }), - }, - }, - { - provide: EscrowOperationsService, - useValue: { - createEscrowInitializationOps: jest.fn().mockReturnValue([]), - createFundingOps: jest.fn().mockReturnValue([]), - createMilestoneReleaseOps: jest.fn().mockReturnValue([]), - createConfirmationOps: jest.fn().mockReturnValue([]), - createCancelOps: jest.fn().mockReturnValue([]), - createCompletionOps: jest.fn().mockReturnValue([]), - createResolveDisputeOps: jest.fn().mockReturnValue([]), - }, - }, - { - provide: getRepositoryToken(Escrow), - useValue: { - findOne: jest.fn(), - }, - }, - { - provide: getRepositoryToken(Party), - useValue: {}, - }, - { - provide: getRepositoryToken(Condition), - useValue: {}, - }, - ], - }).compile(); - - service = module.get( - EscrowStellarIntegrationService, - ); - escrowRepo = module.get(getRepositoryToken(Escrow)); - stellarService = module.get(StellarService); - escrowOps = module.get(EscrowOperationsService); - }); - - const mockEscrow = { - id: 'e1', - amount: 100, - parties: [ - { role: 'buyer', user: { walletAddress: 'buyer-addr' } }, - { role: 'seller', user: { walletAddress: 'seller-addr' } }, - ], - conditions: [{ description: 'c1' }], - assetCode: 'XLM', - assetIssuer: null, - expiresAt: new Date(), - metadataHash: '0'.repeat(64), - }; - - describe('createOnChainEscrow', () => { - it('should build and submit transaction', async () => { - escrowRepo.findOne.mockResolvedValue(mockEscrow); - const hash = await service.createOnChainEscrow('e1'); - expect(hash).toBe('tx-hash'); - expect(stellarService.submitTransaction).toHaveBeenCalled(); - }); - - it('should throw if escrow not found', async () => { - escrowRepo.findOne.mockResolvedValue(null); - await expect(service.createOnChainEscrow('e1')).rejects.toThrow( - 'not found', - ); - }); - }); - - describe('fundOnChainEscrow', () => { - it('should fund successfully', async () => { - const hash = await service.fundOnChainEscrow( - 'e1', - 'funder-pubkey', - '100', - ); - expect(hash).toBe('tx-hash'); - expect(escrowOps.createFundingOps).toHaveBeenCalledWith('e1'); - }); - }); - - describe('releaseMilestonePayment', () => { - it('should release payment successfully', async () => { - const hash = await service.releaseMilestonePayment( - 'e1', - 0, - 'releaser-pubkey', - ); - expect(hash).toBe('tx-hash'); - expect(escrowOps.createMilestoneReleaseOps).toHaveBeenCalledWith('e1', 0); - }); - }); - - describe('confirmEscrow', () => { - it('should confirm successfully', async () => { - const hash = await service.confirmEscrow('e1', 'confirmer-pubkey', 0); - expect(hash).toBe('tx-hash'); - expect(escrowOps.createConfirmationOps).toHaveBeenCalledWith( - 'e1', - 'confirmer-pubkey', - 0, - ); - }); - }); - - describe('cancelOnChainEscrow', () => { - it('should cancel successfully', async () => { - const hash = await service.cancelOnChainEscrow('e1', 'canceller-pubkey'); - expect(hash).toBe('tx-hash'); - expect(escrowOps.createCancelOps).toHaveBeenCalledWith('e1'); - }); - }); - - describe('completeOnChainEscrow', () => { - it('should complete successfully', async () => { - const hash = await service.completeOnChainEscrow( - 'e1', - 'completer-pubkey', - ); - expect(hash).toBe('tx-hash'); - expect(escrowOps.createCompletionOps).toHaveBeenCalledWith('e1'); - }); - }); - - describe('monitorOnChainEscrow', () => { - it('should call streamTransactions', () => { - const callback = jest.fn(); - service.monitorOnChainEscrow('e1', 'pubkey', callback); - expect(stellarService.streamTransactions).toHaveBeenCalledWith( - 'pubkey', - expect.any(Function), - ); - }); - }); - - describe('resolveOnChainDispute', () => { - it('should resolve dispute successfully', async () => { - const hash = await service.resolveOnChainDispute( - 'e1', - 'winner-pubkey', - 'arbitrator-pubkey', - '50', - ); - expect(hash).toBe('tx-hash'); - expect(escrowOps.createResolveDisputeOps).toHaveBeenCalledWith( - 'e1', - 'winner-pubkey', - '50', - ); - }); - }); -}); diff --git a/apps/backend/src/modules/escrow/services/escrow-stellar-integration.service.ts b/apps/backend/src/modules/escrow/services/escrow-stellar-integration.service.ts deleted file mode 100644 index 9708f0bb..00000000 --- a/apps/backend/src/modules/escrow/services/escrow-stellar-integration.service.ts +++ /dev/null @@ -1,466 +0,0 @@ -import { Injectable, Logger, Inject } from '@nestjs/common'; -import { ConfigType } from '@nestjs/config'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { Escrow } from '../entities/escrow.entity'; -import { Party } from '../entities/party.entity'; -import { Condition } from '../entities/condition.entity'; -import { StellarService } from '../../../services/stellar.service'; -import { EscrowOperationsService } from '../../../services/stellar/escrow-operations'; -import stellarConfig from '../../../config/stellar.config'; -import { - StellarSubmitTransactionResponse, - StellarTransactionResponse, -} from '../../../types/stellar.types'; -import * as StellarSdk from '@stellar/stellar-sdk'; - -@Injectable() -export class EscrowStellarIntegrationService { - private readonly logger = new Logger(EscrowStellarIntegrationService.name); - - constructor( - @Inject(stellarConfig.KEY) - private config: ConfigType, - private stellarService: StellarService, - private escrowOperationsService: EscrowOperationsService, - @InjectRepository(Escrow) - private escrowRepository: Repository, - @InjectRepository(Party) - private partyRepository: Repository, - @InjectRepository(Condition) - private conditionRepository: Repository, - ) {} - - /** - * Creates an escrow contract on the Stellar blockchain - * @param escrowId The ID of the escrow to create on-chain - * @returns Transaction hash of the creation transaction - */ - async createOnChainEscrow(escrowId: string): Promise { - try { - this.logger.log(`Creating on-chain escrow for ID: ${escrowId}`); - - // Get the escrow from the database - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - relations: ['parties', 'parties.user', 'conditions'], - }); - - if (!escrow) { - throw new Error(`Escrow with ID ${escrowId} not found`); - } - - if (!escrow.metadataHash) { - throw new Error( - `Escrow ${escrowId} is missing metadataHash for on-chain creation`, - ); - } - - // Get the depositor (usually the buyer) - const depositor = escrow.parties.find( - (party) => party.role === ('buyer' as any), - ); - if (!depositor) { - throw new Error(`Depositor not found for escrow ${escrowId}`); - } - - // Get the recipient (usually the seller) - const recipient = escrow.parties.find( - (party) => party.role === ('seller' as any), - ); - if (!recipient) { - throw new Error(`Recipient not found for escrow ${escrowId}`); - } - - // Convert conditions to milestones format - const milestones = escrow.conditions.map((condition, index) => ({ - id: index, - amount: ( - parseFloat(escrow.amount.toString()) / escrow.conditions.length - ).toString(), - description: condition.description, - })); - - // Create operations for escrow initialization - const operations = - this.escrowOperationsService.createEscrowInitializationOps( - escrowId, - depositor.user.walletAddress, // User's Stellar wallet address - recipient.user.walletAddress, // User's Stellar wallet address - escrow.assetCode === 'XLM' - ? 'native' - : new StellarSdk.Asset( - escrow.assetCode, - escrow.assetIssuer, - ).contractId(this.config.networkPassphrase), - milestones, - escrow.expiresAt - ? Math.floor(new Date(escrow.expiresAt).getTime() / 1000) - : Math.floor(Date.now() / 1000) + 86400, // Convert to Unix timestamp or default to 24 hours - escrow.metadataHash, - ); - - // Build the transaction - const transaction = await this.stellarService.buildTransaction( - depositor.user.walletAddress, // Source account - operations, - ); - - // Submit the transaction to the Stellar network - const result: StellarSubmitTransactionResponse = - await this.stellarService.submitTransaction(transaction); - - this.logger.log( - `Successfully created on-chain escrow ${escrowId}, transaction: ${result.hash}`, - ); - return result.hash; - } catch (error) { - this.logger.error( - `Failed to create on-chain escrow ${escrowId}: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Funds an escrow on the Stellar blockchain - * @param escrowId The ID of the escrow to fund - * @param funderPublicKey The public key of the account funding the escrow - * @param amount The amount to fund - * @param assetCode The asset code (e.g., 'XLM' or custom asset) - * @returns Transaction hash of the funding transaction - */ - async fundOnChainEscrow( - escrowId: string, - funderPublicKey: string, - amount: string, - assetCode: string = 'XLM', - assetIssuer?: string, - ): Promise { - try { - this.logger.log( - `Funding on-chain escrow ${escrowId} with ${amount} ${assetCode}`, - ); - - // Verify funder has sufficient balance and trustline - const funderAccount = - await this.stellarService.getAccount(funderPublicKey); - const balanceItem = funderAccount.balances.find((b) => { - if (assetCode === 'XLM' || assetCode === 'native') { - return b.asset_type === 'native'; - } else { - return b.asset_code === assetCode && b.asset_issuer === assetIssuer; - } - }); - - if (!balanceItem) { - if (assetCode === 'XLM') { - throw new Error( - 'Funder account has no XLM balance or does not exist', - ); - } else { - throw new Error( - `Funder does not have a trustline for the asset ${assetCode}. Please establish a trustline first.`, - ); - } - } - - if (parseFloat(balanceItem.balance) < parseFloat(amount)) { - throw new Error( - `Insufficient balance. Funder has ${balanceItem.balance} ${assetCode}, needs ${amount}`, - ); - } - - // Create funding operations - const operations = - this.escrowOperationsService.createFundingOps(escrowId); - - // Build the transaction - const transaction = await this.stellarService.buildTransaction( - funderPublicKey, // Source account - operations, - ); - - // Submit the transaction to the Stellar network - const result: StellarSubmitTransactionResponse = - await this.stellarService.submitTransaction(transaction); - - this.logger.log( - `Successfully funded escrow ${escrowId}, transaction: ${result.hash}`, - ); - return result.hash; - } catch (error) { - this.logger.error( - `Failed to fund on-chain escrow ${escrowId}: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Releases a milestone payment on the Stellar blockchain - * @param escrowId The ID of the escrow - * @param milestoneId The ID of the milestone to release - * @param releaserPublicKey The public key of the account releasing the payment - * @param recipientPublicKey The public key of the recipient - * @param amount The amount to release - * @param assetCode The asset code - * @returns Transaction hash of the release transaction - */ - async releaseMilestonePayment( - escrowId: string, - milestoneId: number, - releaserPublicKey: string, - // recipientPublicKey: string, - // amount: string, - // assetCode: string = 'XLM', - ): Promise { - try { - this.logger.log( - `Releasing milestone ${milestoneId} for escrow ${escrowId}`, - ); - - // Create milestone release operations - const operations = this.escrowOperationsService.createMilestoneReleaseOps( - escrowId, - milestoneId, - ); - - // Build the transaction - const transaction = await this.stellarService.buildTransaction( - releaserPublicKey, // Source account - operations, - ); - - // Submit the transaction to the Stellar network - const result: StellarSubmitTransactionResponse = - await this.stellarService.submitTransaction(transaction); - - this.logger.log( - `Successfully released milestone ${milestoneId} for escrow ${escrowId}, transaction: ${result.hash}`, - ); - return result.hash; - } catch (error) { - this.logger.error( - `Failed to release milestone ${milestoneId} for escrow ${escrowId}: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Confirms delivery/acceptance of an escrow on the Stellar blockchain - * @param escrowId The ID of the escrow to confirm - * @param confirmerPublicKey The public key of the account confirming - * @param milestoneId The milestone ID - * @returns Transaction hash of the confirmation transaction - */ - async confirmEscrow( - escrowId: string, - confirmerPublicKey: string, - milestoneId: number, - ): Promise { - try { - this.logger.log( - `Confirming escrow ${escrowId} for milestone: ${milestoneId}`, - ); - - // Create confirmation operations - const operations = this.escrowOperationsService.createConfirmationOps( - escrowId, - confirmerPublicKey, - milestoneId, - ); - - // Build the transaction - const transaction = await this.stellarService.buildTransaction( - confirmerPublicKey, // Source account - operations, - ); - - // Submit the transaction to the Stellar network - const result: StellarSubmitTransactionResponse = - await this.stellarService.submitTransaction(transaction); - - this.logger.log( - `Successfully confirmed milestone ${milestoneId} for escrow ${escrowId}, transaction: ${result.hash}`, - ); - return result.hash; - } catch (error) { - this.logger.error( - `Failed to confirm escrow ${escrowId}: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Cancels an escrow on the Stellar blockchain - * @param escrowId The ID of the escrow to cancel - * @param cancellerPublicKey The public key of the account canceling - * @param refundDestination The destination for refunded funds - * @returns Transaction hash of the cancellation transaction - */ - async cancelOnChainEscrow( - escrowId: string, - cancellerPublicKey: string, - - // refundDestination: string, - ): Promise { - try { - this.logger.log(`Canceling on-chain escrow ${escrowId}`); - - // Create cancel operations - const operations = this.escrowOperationsService.createCancelOps(escrowId); - - // Build the transaction - const transaction = await this.stellarService.buildTransaction( - cancellerPublicKey, // Source account - operations, - ); - - // Submit the transaction to the Stellar network - const result: StellarSubmitTransactionResponse = - await this.stellarService.submitTransaction(transaction); - - this.logger.log( - `Successfully canceled escrow ${escrowId}, transaction: ${result.hash}`, - ); - return result.hash; - } catch (error) { - this.logger.error( - `Failed to cancel on-chain escrow ${escrowId}: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Completes an escrow on the Stellar blockchain - * @param escrowId The ID of the escrow to complete - * @param completerPublicKey The public key of the account completing - * @returns Transaction hash of the completion transaction - */ - async completeOnChainEscrow( - escrowId: string, - completerPublicKey: string, - ): Promise { - try { - this.logger.log(`Completing on-chain escrow ${escrowId}`); - - // Create completion operations - const operations = - this.escrowOperationsService.createCompletionOps(escrowId); - - // Build the transaction - const transaction = await this.stellarService.buildTransaction( - completerPublicKey, // Source account - operations, - ); - - // Submit the transaction to the Stellar network - const result: StellarSubmitTransactionResponse = - await this.stellarService.submitTransaction(transaction); - - this.logger.log( - `Successfully completed escrow ${escrowId}, transaction: ${result.hash}`, - ); - return result.hash; - } catch (error) { - this.logger.error( - `Failed to complete on-chain escrow ${escrowId}: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Monitors the status of an on-chain escrow - * @param escrowId The ID of the escrow to monitor - * @param accountPublicKey The public key of the account to monitor - * @param callback Callback function to handle state changes - * @returns EventSource object for stream control - */ - monitorOnChainEscrow( - escrowId: string, - accountPublicKey: string, - callback: (transaction: StellarTransactionResponse) => void, - ): EventSource { - this.logger.log( - `Starting to monitor on-chain escrow ${escrowId} for account: ${accountPublicKey}`, - ); - - // Create a wrapper callback that filters for our escrow-related transactions - const filteredCallback = (transaction: StellarTransactionResponse) => { - // Check if this transaction relates to our escrow - const isEscrowRelated = - transaction.memo && - typeof transaction.memo === 'string' && - transaction.memo.includes(escrowId); - - if (isEscrowRelated) { - this.logger.log( - `Detected escrow ${escrowId} related transaction: ${transaction.hash}`, - ); - callback(transaction); - } - }; - - // Stream transactions for the account - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return this.stellarService.streamTransactions( - accountPublicKey, - filteredCallback, - ); - } - - async resolveOnChainDispute( - escrowId: string, - winnerPublicKey: string, - arbitratorPublicKey: string, - splitWinnerAmount?: string, - ): Promise { - try { - this.logger.log( - `Resolving on-chain dispute for escrow ${escrowId} in favor of ${winnerPublicKey}`, - ); - - const operations = this.escrowOperationsService.createResolveDisputeOps( - escrowId, - winnerPublicKey, - splitWinnerAmount, - ); - - const transaction = await this.stellarService.buildTransaction( - arbitratorPublicKey, - operations, - ); - - const result = await this.stellarService.submitTransaction(transaction); - - this.logger.log( - `Successfully resolved on-chain dispute for escrow ${escrowId}, transaction: ${result.hash}`, - ); - return result.hash; - } catch (error) { - this.logger.error( - `Failed to resolve on-chain dispute for escrow ${escrowId}: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Safely extracts error message from unknown error type - */ - private getErrorMessage(error: unknown): string { - if (error instanceof Error) { - return error.message; - } - if (typeof error === 'object' && error !== null && 'message' in error) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return String((error as any).message); - } - return 'Unknown error'; - } -} diff --git a/apps/backend/src/modules/escrow/services/escrow.service.spec.ts b/apps/backend/src/modules/escrow/services/escrow.service.spec.ts deleted file mode 100644 index 962cc475..00000000 --- a/apps/backend/src/modules/escrow/services/escrow.service.spec.ts +++ /dev/null @@ -1,520 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { Repository, UpdateResult } from 'typeorm'; - -import { EscrowService } from './escrow.service'; -import { Escrow, EscrowStatus, EscrowType } from '../entities/escrow.entity'; -import { Party, PartyRole, PartyStatus } from '../entities/party.entity'; -import { Condition, ConditionType } from '../entities/condition.entity'; -import { EscrowEvent } from '../entities/escrow-event.entity'; -import { - Dispute, - DisputeStatus, - DisputeOutcome, -} from '../entities/dispute.entity'; - -import { FulfillConditionDto } from '../dto/fulfill-condition.dto'; -import { CreateEscrowDto } from '../dto/create-escrow.dto'; - -import { - BadRequestException, - ConflictException, - ForbiddenException, - NotFoundException, - UnprocessableEntityException, -} from '@nestjs/common'; - -import { EscrowStellarIntegrationService } from './escrow-stellar-integration.service'; -import { WebhookService } from '../../../services/webhook/webhook.service'; -import { IpfsService } from '../../ipfs/ipfs.service'; -import { AllowedAsset } from '../../assets/entities/allowed-asset.entity'; -import { User, UserRole } from '../../user/entities/user.entity'; -import { NotificationService } from '../../../notifications/notifications.service'; -import { NotificationEventType } from '../../../notifications/enums/notification-event.enum'; - -// ✅ FIX: missing services -import { EscrowLifecycleService } from '../escrow-lifecycle.service'; -import { EscrowFundingService } from '../escrow-funding.service'; -import { EscrowDisputeService } from '../escrow-dispute.service'; -import { EscrowQueryService } from '../escrow-query.service'; -import { StellarService } from '../../../services/stellar.service'; - -describe('EscrowService', () => { - let service: EscrowService; - let escrowRepository: jest.Mocked>; - let partyRepository: jest.Mocked>; - let conditionRepository: jest.Mocked>; - let eventRepository: jest.Mocked>; - let disputeRepository: jest.Mocked>; - let userRepository: jest.Mocked>; - let assetRepository: jest.Mocked>; - - let ipfsService: { uploadFile: jest.Mock; getGatewayUrl: jest.Mock }; - let webhookService: { dispatchEvent: jest.Mock }; - let notificationService: { handleEscrowEvent: jest.Mock }; - - // ✅ NEW MOCKS - let lifecycleService: { - create: jest.Mock; - cancel: jest.Mock; - expire: jest.Mock; - }; - - let fundingService: { - fund: jest.Mock; - }; - - let disputeService: { - fileDispute: jest.Mock; - resolveDispute: jest.Mock; - }; - - let queryService: { - findOverview: jest.Mock; - }; - - const mockEscrow: Partial = { - id: 'escrow-123', - title: 'Test Escrow', - amount: 100, - status: EscrowStatus.PENDING, - type: EscrowType.STANDARD, - creatorId: 'user-123', - parties: [], - conditions: [], - events: [], - createdAt: new Date(), - updatedAt: new Date(), - }; - - const mockParty: Partial = { - id: 'party-123', - escrowId: 'escrow-123', - userId: 'user-456', - role: PartyRole.SELLER, - status: PartyStatus.PENDING, - createdAt: new Date(), - }; - - const mockCondition: Partial = { - id: 'condition-123', - escrowId: 'escrow-123', - description: 'Delivery confirmed', - type: ConditionType.MANUAL, - isFulfilled: false, - isMet: false, - createdAt: new Date(), - updatedAt: new Date(), - }; - - beforeEach(async () => { - // ---------------- MOCK REPOS ---------------- - const mockEscrowRepo = { - create: jest.fn(), - save: jest.fn(), - findOne: jest.fn(), - update: jest.fn(), - createQueryBuilder: jest.fn(), - }; - - const mockPartyRepo = { - create: jest.fn(), - save: jest.fn(), - findOne: jest.fn(), - find: jest.fn(), - }; - - const mockConditionRepo = { - create: jest.fn(), - save: jest.fn(), - findOne: jest.fn(), - }; - - const mockEventRepo = { - create: jest.fn(), - save: jest.fn(), - }; - - const mockDisputeRepo = { - create: jest.fn(), - save: jest.fn(), - findOne: jest.fn(), - }; - - const mockUserRepo = { - findOne: jest.fn(), - }; - - const mockAssetRepo = { - findOne: jest.fn(), - find: jest.fn(), - }; - - const mockIpfsService = { - uploadFile: jest.fn().mockResolvedValue('mock-cid'), - getGatewayUrl: jest.fn().mockReturnValue('https://ipfs.io/ipfs/mock-cid'), - }; - - const mockNotificationService = { - handleEscrowEvent: jest.fn().mockResolvedValue(undefined), - }; - - // ---------------- NEW SERVICE MOCKS ---------------- - const mockEscrowLifecycleService = { - create: jest.fn(), - cancel: jest.fn(), - expire: jest.fn(), - }; - - const mockFundingService = { - fund: jest.fn(), - }; - - const mockDisputeService = { - fileDispute: jest.fn(), - resolveDispute: jest.fn(), - }; - - const mockQueryService = { - findOverview: jest.fn(), - }; - - const module: TestingModule = await Test.createTestingModule({ - providers: [ - EscrowService, - { provide: getRepositoryToken(Escrow), useValue: mockEscrowRepo }, - { provide: getRepositoryToken(Party), useValue: mockPartyRepo }, - { provide: getRepositoryToken(Condition), useValue: mockConditionRepo }, - { provide: getRepositoryToken(EscrowEvent), useValue: mockEventRepo }, - { provide: getRepositoryToken(Dispute), useValue: mockDisputeRepo }, - { provide: getRepositoryToken(User), useValue: mockUserRepo }, - { provide: getRepositoryToken(AllowedAsset), useValue: mockAssetRepo }, - - { provide: IpfsService, useValue: mockIpfsService }, - { provide: NotificationService, useValue: mockNotificationService }, - - { - provide: EscrowStellarIntegrationService, - useValue: { - completeOnChainEscrow: jest.fn(), - fundOnChainEscrow: jest.fn(), - }, - }, - { - provide: WebhookService, - useValue: { - dispatchEvent: jest.fn(), - }, - }, - - // ✅ CRITICAL FIXES - { - provide: EscrowLifecycleService, - useValue: mockEscrowLifecycleService, - }, - { - provide: EscrowFundingService, - useValue: mockFundingService, - }, - { - provide: EscrowDisputeService, - useValue: mockDisputeService, - }, - { - provide: EscrowQueryService, - useValue: mockQueryService, - }, - { - provide: StellarService, - useValue: { - getAccount: jest.fn().mockResolvedValue({ - balances: [{ asset_type: 'native', balance: '1000' }], - }), - }, - }, - ], - }).compile(); - - // ---------------- ASSIGN ---------------- - service = module.get(EscrowService); - - escrowRepository = module.get(getRepositoryToken(Escrow)); - partyRepository = module.get(getRepositoryToken(Party)); - conditionRepository = module.get(getRepositoryToken(Condition)); - eventRepository = module.get(getRepositoryToken(EscrowEvent)); - disputeRepository = module.get(getRepositoryToken(Dispute)); - userRepository = module.get(getRepositoryToken(User)); - assetRepository = module.get(getRepositoryToken(AllowedAsset)); - - ipfsService = module.get(IpfsService); - webhookService = module.get(WebhookService); - notificationService = module.get(NotificationService); - - lifecycleService = module.get(EscrowLifecycleService); - fundingService = module.get(EscrowFundingService); - disputeService = module.get(EscrowDisputeService); - queryService = module.get(EscrowQueryService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); - - describe('acceptPartyInvitation', () => { - const pendingParty = { - ...mockParty, - status: PartyStatus.PENDING, - respondedAt: null, - escrow: { - id: 'escrow-123', - title: 'Test Escrow', - status: EscrowStatus.PENDING, - creatorId: 'user-123', - }, - } as Party; - - it('sets status to ACCEPTED and records respondedAt', async () => { - partyRepository.findOne.mockResolvedValue({ - ...pendingParty, - status: PartyStatus.PENDING, - }); - partyRepository.save.mockResolvedValue({ - ...pendingParty, - status: PartyStatus.ACCEPTED, - }); - eventRepository.create.mockReturnValue({} as any); - eventRepository.save.mockResolvedValue({} as any); - userRepository.findOne.mockResolvedValue({ - id: 'user-456', - email: 'seller@test.com', - } as User); - - const result = await service.acceptPartyInvitation( - 'escrow-123', - 'party-123', - 'user-456', - ); - - expect(result.status).toBe(PartyStatus.ACCEPTED); - expect(partyRepository.save).toHaveBeenCalledWith( - expect.objectContaining({ - status: PartyStatus.ACCEPTED, - respondedAt: expect.any(Date), - }), - ); - }); - - it('notifies the escrow creator on acceptance', async () => { - partyRepository.findOne.mockResolvedValue({ - ...pendingParty, - status: PartyStatus.PENDING, - }); - partyRepository.save.mockResolvedValue({ - ...pendingParty, - status: PartyStatus.ACCEPTED, - }); - eventRepository.create.mockReturnValue({} as any); - eventRepository.save.mockResolvedValue({} as any); - userRepository.findOne.mockResolvedValue({ - id: 'user-456', - email: 'seller@test.com', - } as User); - - await service.acceptPartyInvitation( - 'escrow-123', - 'party-123', - 'user-456', - ); - - await new Promise(process.nextTick); // flush fire-and-forget - expect(notificationService.handleEscrowEvent).toHaveBeenCalledWith( - 'user-123', - NotificationEventType.PARTY_ACCEPTED, - expect.objectContaining({ escrowId: 'escrow-123' }), - ); - }); - - it('throws ForbiddenException when user is not the party', async () => { - partyRepository.findOne.mockResolvedValue({ - ...pendingParty, - status: PartyStatus.PENDING, - }); - - await expect( - service.acceptPartyInvitation('escrow-123', 'party-123', 'wrong-user'), - ).rejects.toThrow(ForbiddenException); - }); - - it('throws BadRequestException when invitation is already responded', async () => { - partyRepository.findOne.mockResolvedValue({ - ...pendingParty, - status: PartyStatus.ACCEPTED, - }); - - await expect( - service.acceptPartyInvitation('escrow-123', 'party-123', 'user-456'), - ).rejects.toThrow(BadRequestException); - }); - - it('throws NotFoundException when party does not exist', async () => { - partyRepository.findOne.mockResolvedValue(null); - - await expect( - service.acceptPartyInvitation('escrow-123', 'nonexistent', 'user-456'), - ).rejects.toThrow(NotFoundException); - }); - }); - - describe('rejectPartyInvitation', () => { - const pendingSellerParty = { - ...mockParty, - status: PartyStatus.PENDING, - respondedAt: null, - escrow: { - id: 'escrow-123', - title: 'Test Escrow', - status: EscrowStatus.PENDING, - creatorId: 'user-123', - }, - } as Party; - - it('sets status to REJECTED and records respondedAt', async () => { - partyRepository.findOne.mockResolvedValue({ - ...pendingSellerParty, - status: PartyStatus.PENDING, - }); - partyRepository.save.mockResolvedValue({ - ...pendingSellerParty, - status: PartyStatus.REJECTED, - }); - eventRepository.create.mockReturnValue({} as any); - eventRepository.save.mockResolvedValue({} as any); - escrowRepository.update = jest.fn().mockResolvedValue({}); - userRepository.findOne.mockResolvedValue({ - id: 'user-456', - email: 'seller@test.com', - } as User); - - const result = await service.rejectPartyInvitation( - 'escrow-123', - 'party-123', - 'user-456', - ); - - expect(result.status).toBe(PartyStatus.REJECTED); - expect(partyRepository.save).toHaveBeenCalledWith( - expect.objectContaining({ - status: PartyStatus.REJECTED, - respondedAt: expect.any(Date), - }), - ); - }); - - it('auto-cancels the escrow when a required party rejects', async () => { - partyRepository.findOne.mockResolvedValue({ - ...pendingSellerParty, - status: PartyStatus.PENDING, - }); - partyRepository.save.mockResolvedValue({ - ...pendingSellerParty, - status: PartyStatus.REJECTED, - }); - eventRepository.create.mockReturnValue({} as any); - eventRepository.save.mockResolvedValue({} as any); - const updateMock = jest.fn().mockResolvedValue({}); - escrowRepository.update = updateMock; - webhookService.dispatchEvent = jest.fn().mockResolvedValue(undefined); - userRepository.findOne.mockResolvedValue({ - id: 'user-456', - email: undefined, - } as User); - - await service.rejectPartyInvitation( - 'escrow-123', - 'party-123', - 'user-456', - ); - - expect(updateMock).toHaveBeenCalledWith( - 'escrow-123', - expect.objectContaining({ status: EscrowStatus.CANCELLED }), - ); - }); - - it('throws ForbiddenException when user is not the party', async () => { - partyRepository.findOne.mockResolvedValue({ - ...pendingSellerParty, - status: PartyStatus.PENDING, - }); - - await expect( - service.rejectPartyInvitation('escrow-123', 'party-123', 'wrong-user'), - ).rejects.toThrow(ForbiddenException); - }); - }); - - describe('getPendingInvitations', () => { - it('returns pending party invitations for user', async () => { - const pendingParties = [ - { - ...mockParty, - status: PartyStatus.PENDING, - escrow: { id: 'escrow-123' }, - }, - ]; - partyRepository.find.mockResolvedValue(pendingParties as Party[]); - - const result = await service.getPendingInvitations('user-456'); - - expect(partyRepository.find).toHaveBeenCalledWith( - expect.objectContaining({ - where: { userId: 'user-456', status: PartyStatus.PENDING }, - }), - ); - expect(result).toHaveLength(1); - }); - - it('returns empty array when no pending invitations', async () => { - partyRepository.find.mockResolvedValue([]); - - const result = await service.getPendingInvitations('user-456'); - - expect(result).toHaveLength(0); - }); - }); - - describe('fund - party acceptance gate', () => { - it('throws BadRequestException when seller has not accepted', async () => { - const escrowWithPendingSeller = { - ...mockEscrow, - status: EscrowStatus.PENDING, - stellarTxHash: undefined, - amount: 100, - parties: [ - { - role: PartyRole.BUYER, - status: PartyStatus.ACCEPTED, - userId: 'user-123', - }, - { - role: PartyRole.SELLER, - status: PartyStatus.PENDING, - userId: 'user-456', - }, - ], - }; - escrowRepository.findOne.mockResolvedValue( - escrowWithPendingSeller as Escrow, - ); - - await expect( - service.fund( - 'escrow-123', - { amount: 100 } as any, - 'user-123', - 'wallet-addr', - ), - ).rejects.toThrow(BadRequestException); - }); - }); -}); diff --git a/apps/backend/src/modules/escrow/services/escrow.service.ts b/apps/backend/src/modules/escrow/services/escrow.service.ts deleted file mode 100644 index 880d5daa..00000000 --- a/apps/backend/src/modules/escrow/services/escrow.service.ts +++ /dev/null @@ -1,1561 +0,0 @@ -import { - Injectable, - NotFoundException, - BadRequestException, - ForbiddenException, - ConflictException, - UnprocessableEntityException, -} from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Brackets, Repository, SelectQueryBuilder } from 'typeorm'; -import { Escrow, EscrowStatus, EscrowType } from '../entities/escrow.entity'; -import { Party, PartyRole, PartyStatus } from '../entities/party.entity'; -import { Condition } from '../entities/condition.entity'; -import { EscrowEvent, EscrowEventType } from '../entities/escrow-event.entity'; -import { - Dispute, - DisputeStatus, - DisputeOutcome, -} from '../entities/dispute.entity'; -import { CreateEscrowDto } from '../dto/create-escrow.dto'; -import { UpdateEscrowDto } from '../dto/update-escrow.dto'; -import { ListEscrowsDto, SortOrder } from '../dto/list-escrows.dto'; -import { ListEventsDto, EventSortOrder } from '../dto/list-events.dto'; -import { EventResponseDto } from '../dto/event-response.dto'; -import { CancelEscrowDto } from '../dto/cancel-escrow.dto'; -import { - EscrowOverviewQueryDto, - EscrowOverviewResponseDto, - EscrowOverviewRole, - EscrowOverviewSortBy, - EscrowOverviewSortOrder, - EscrowOverviewStatus, -} from '../dto/escrow-overview.dto'; -import { FulfillConditionDto } from '../dto/fulfill-condition.dto'; -import { FileDisputeDto, ResolveDisputeDto } from '../dto/dispute.dto'; -import { FundEscrowDto } from '../dto/fund-escrow.dto'; -import { ExpireEscrowDto } from '../dto/expire-escrow.dto'; -import { ProposeMilestoneChangeDto } from '../dto/milestone-change.dto'; -import { validateTransition, isTerminalStatus } from '../escrow-state-machine'; -import { EscrowStellarIntegrationService } from './escrow-stellar-integration.service'; -import { WebhookService } from '../../../services/webhook/webhook.service'; -import { User, UserRole } from '../../user/entities/user.entity'; -import { IpfsService } from '../../ipfs/ipfs.service'; -import { AllowedAsset } from '../../assets/entities/allowed-asset.entity'; -import { NotificationService } from '../../../notifications/notifications.service'; -import { NotificationEventType } from '../../../notifications/enums/notification-event.enum'; - -@Injectable() -export class EscrowService { - constructor( - @InjectRepository(Escrow) - private escrowRepository: Repository, - @InjectRepository(Party) - private partyRepository: Repository, - @InjectRepository(Condition) - private conditionRepository: Repository, - @InjectRepository(EscrowEvent) - private eventRepository: Repository, - @InjectRepository(Dispute) - private disputeRepository: Repository, - @InjectRepository(User) - private userRepository: Repository, - @InjectRepository(AllowedAsset) - private assetRepository: Repository, - - private readonly stellarIntegrationService: EscrowStellarIntegrationService, - private readonly webhookService: WebhookService, - private readonly ipfsService: IpfsService, - private readonly notificationService: NotificationService, - ) {} - - async create( - dto: CreateEscrowDto, - creatorId: string, - ipAddress?: string, - ): Promise { - try { - const escrow = this.escrowRepository.create({ - title: dto.title, - description: dto.description, - amount: dto.amount, - assetCode: dto.asset?.code || 'XLM', - assetIssuer: dto.asset?.issuer || undefined, - type: dto.type, - creatorId, - expiresAt: dto.expiresAt ? new Date(dto.expiresAt) : undefined, - metadataHash: dto.metadataHash, - } as Partial); - - const savedEscrow = (await this.escrowRepository.save( - escrow, - )) as unknown as Escrow; - - const parties = dto.parties.map((partyDto) => - this.partyRepository.create({ - escrowId: savedEscrow.id, - userId: partyDto.userId, - role: partyDto.role, - }), - ); - await this.partyRepository.save(parties); - - // Notify each invited party (fire-and-forget; failures must not block escrow creation) - for (const partyDto of dto.parties) { - const invitedUser = await this.userRepository.findOne({ - where: { id: partyDto.userId }, - }); - this.notificationService - .handleEscrowEvent( - partyDto.userId, - NotificationEventType.PARTY_INVITED, - { - escrowId: savedEscrow.id, - escrowTitle: savedEscrow.title, - role: partyDto.role, - invitedBy: creatorId, - email: invitedUser?.email ?? undefined, - }, - ) - .catch(() => undefined); - } - - if (dto.conditions && dto.conditions.length > 0) { - const conditions = dto.conditions.map((conditionDto) => - this.conditionRepository.create({ - escrowId: savedEscrow.id, - description: conditionDto.description, - type: conditionDto.type, - metadata: conditionDto.metadata, - }), - ); - await this.conditionRepository.save(conditions); - } - - await this.logEvent( - savedEscrow.id, - EscrowEventType.CREATED, - creatorId, - { dto }, - ipAddress, - ); - - // Dispatch webhook for escrow.created - await this.webhookService.dispatchEvent('escrow.created', { - escrowId: savedEscrow.id, - }); - - return await this.findOne(savedEscrow.id); - } catch (e) { - console.error('ESCROW_CREATE_ERROR_TRACE:', e); - throw e; - } - } - - async findOverview( - userId: string, - query: EscrowOverviewQueryDto, - ): Promise { - const page = query.page ?? 1; - const pageSize = query.pageSize ?? 20; - const role = query.role ?? EscrowOverviewRole.ANY; - const sortBy = query.sortBy ?? EscrowOverviewSortBy.CREATED_AT; - const sortOrder = - query.sortOrder === EscrowOverviewSortOrder.ASC ? 'ASC' : 'DESC'; - - const qb = this.escrowRepository.createQueryBuilder('escrow'); - - qb.select([ - 'escrow.id AS escrowId', - 'escrow.creatorId AS depositor', - 'escrow.assetCode AS token', - 'escrow.assetIssuer AS tokenIssuer', - 'escrow.amount AS totalAmount', - 'escrow.status AS status', - 'escrow.expiresAt AS deadline', - 'escrow.createdAt AS createdAt', - 'escrow.updatedAt AS updatedAt', - ]) - .addSelect( - `CASE WHEN escrow.isReleased = 1 OR escrow.status = :completedStatus THEN escrow.amount ELSE 0 END`, - 'totalReleased', - ) - .addSelect( - `CASE WHEN escrow.isReleased = 1 OR escrow.status = :completedStatus THEN 0 ELSE escrow.amount END`, - 'remainingAmount', - ) - .addSelect( - (recipientSubquery) => - recipientSubquery - .select('recipientParty.userId') - .from(Party, 'recipientParty') - .where('recipientParty.escrowId = escrow.id') - .andWhere('recipientParty.role = :recipientRole') - .limit(1), - 'recipient', - ) - .leftJoin( - AllowedAsset, - 'assetInfo', - 'assetInfo.code = escrow.assetCode AND (assetInfo.issuer = escrow.assetIssuer OR (assetInfo.issuer IS NULL AND escrow.assetIssuer IS NULL))', - ) - .addSelect('COALESCE(assetInfo.decimals, 7)', 'tokenDecimals') - .setParameter('completedStatus', EscrowStatus.COMPLETED) - .setParameter('recipientRole', PartyRole.SELLER); - - const recipientExistsSubquery = qb - .subQuery() - .select('1') - .from(Party, 'partyFilter') - .where('partyFilter.escrowId = escrow.id') - .andWhere('partyFilter.userId = :userId') - .andWhere('partyFilter.role = :recipientRole') - .getQuery(); - - if (role === EscrowOverviewRole.DEPOSITOR) { - qb.where('escrow.creatorId = :userId', { userId }); - } else if (role === EscrowOverviewRole.RECIPIENT) { - qb.where(`EXISTS ${recipientExistsSubquery}`, { userId }); - } else { - qb.where( - new Brackets((overviewScope) => { - overviewScope - .where('escrow.creatorId = :userId', { userId }) - .orWhere(`EXISTS ${recipientExistsSubquery}`, { userId }); - }), - ); - } - - if (query.status) { - if (query.status === EscrowOverviewStatus.EXPIRED) { - qb.andWhere('escrow.expiresAt IS NOT NULL') - .andWhere('escrow.expiresAt < :now', { now: new Date() }) - .andWhere('escrow.status IN (:...expirableStatuses)', { - expirableStatuses: [EscrowStatus.PENDING, EscrowStatus.ACTIVE], - }); - } else if (query.status === EscrowOverviewStatus.CREATED) { - qb.andWhere('escrow.status = :status', { - status: EscrowStatus.PENDING, - }); - } else { - qb.andWhere('escrow.status = :status', { status: query.status }); - } - } - - if (query.token) { - qb.andWhere('escrow.assetCode = :asset', { asset: query.token }); - } - - if (query.from) { - qb.andWhere('escrow.createdAt >= :fromDate', { - fromDate: new Date(query.from), - }); - } - - if (query.to) { - qb.andWhere('escrow.createdAt <= :toDate', { - toDate: new Date(query.to), - }); - } - - if (sortBy === EscrowOverviewSortBy.DEADLINE) { - qb.orderBy('escrow.expiresAt', sortOrder); - } else { - qb.orderBy('escrow.createdAt', sortOrder); - } - - const totalItems = await qb.getCount(); - const rows = await qb - .offset((page - 1) * pageSize) - .limit(pageSize) - .getRawMany<{ - escrowId: string; - depositor: string; - recipient: string | null; - token: string; - tokenIssuer: string | null; - tokenDecimals: number; - totalAmount: string | number; - totalReleased: string | number; - remainingAmount: string | number; - status: string; - deadline: Date | null; - createdAt: Date; - updatedAt: Date; - }>(); - - return { - data: rows.map((row) => ({ - escrowId: row.escrowId, - depositor: row.depositor, - recipient: row.recipient, - token: row.token, - tokenIssuer: row.tokenIssuer || undefined, - tokenDecimals: row.tokenDecimals, - totalAmount: parseFloat(row.totalAmount.toString()), - totalReleased: parseFloat(row.totalReleased.toString()), - remainingAmount: parseFloat(row.remainingAmount.toString()), - status: row.status, - deadline: row.deadline, - createdAt: row.createdAt, - updatedAt: row.updatedAt, - })), - totalItems, - totalPages: Math.ceil(totalItems / pageSize), - page, - pageSize, - }; - } - - async findAll( - userId: string, - query: ListEscrowsDto, - ): Promise<{ data: Escrow[]; total: number; page: number; limit: number }> { - const page = query.page || 1; - const limit = query.limit || 10; - const skip = (page - 1) * limit; - - const qb: SelectQueryBuilder = this.escrowRepository - .createQueryBuilder('escrow') - .leftJoinAndSelect('escrow.parties', 'party') - .leftJoinAndSelect('escrow.conditions', 'condition') - .where('(escrow.creatorId = :userId OR party.userId = :userId)', { - userId, - }); - - if (query.status) { - qb.andWhere('escrow.status = :status', { status: query.status }); - } - - if (query.type) { - qb.andWhere('escrow.type = :type', { type: query.type }); - } - - if (query.role) { - qb.andWhere('party.role = :role', { role: query.role }); - } - - if (query.search) { - qb.andWhere( - '(escrow.title LIKE :search OR escrow.description LIKE :search)', - { search: `%${query.search}%` }, - ); - } - - const sortOrder = query.sortOrder === SortOrder.ASC ? 'ASC' : 'DESC'; - qb.orderBy(`escrow.${query.sortBy || 'createdAt'}`, sortOrder); - - const [data, total] = await qb.skip(skip).take(limit).getManyAndCount(); - - return { data, total, page, limit }; - } - - async findOne(id: string): Promise { - const escrow = await this.escrowRepository.findOne({ - where: { id }, - relations: ['parties', 'conditions', 'events', 'creator'], - }); - - if (!escrow) { - throw new NotFoundException('Escrow not found'); - } - - return escrow; - } - - async update( - id: string, - dto: UpdateEscrowDto, - userId: string, - ipAddress?: string, - ): Promise { - const escrow = await this.findOne(id); - - if (escrow.creatorId !== userId) { - throw new ForbiddenException('Only the creator can update this escrow'); - } - - if (escrow.status !== EscrowStatus.PENDING) { - throw new BadRequestException( - 'Escrow can only be updated while in pending status', - ); - } - - const updateData: Partial = {}; - if (dto.title !== undefined) updateData.title = dto.title; - if (dto.description !== undefined) updateData.description = dto.description; - if (dto.expiresAt !== undefined) - updateData.expiresAt = new Date(dto.expiresAt); - - await this.escrowRepository.update(id, updateData); - - await this.logEvent( - id, - EscrowEventType.UPDATED, - userId, - { changes: dto }, - ipAddress, - ); - // Optionally dispatch webhook for escrow update - - return this.findOne(id); - } - - async cancel( - id: string, - dto: CancelEscrowDto, - userId: string, - ipAddress?: string, - ): Promise { - const escrow = await this.findOne(id); - - if (isTerminalStatus(escrow.status)) { - throw new BadRequestException( - `Cannot cancel an escrow that is already ${escrow.status}`, - ); - } - - if (escrow.status === EscrowStatus.PENDING) { - if (escrow.creatorId !== userId) { - throw new ForbiddenException( - 'Only the creator can cancel a pending escrow', - ); - } - } else if (escrow.status === EscrowStatus.ACTIVE) { - const arbitrator = escrow.parties?.find( - (p) => p.role === PartyRole.ARBITRATOR && p.userId === userId, - ); - if (!arbitrator && escrow.creatorId !== userId) { - throw new ForbiddenException( - 'Only the creator or arbitrator can cancel an active escrow', - ); - } - } - - validateTransition(escrow.status, EscrowStatus.CANCELLED); - - await this.escrowRepository.update(id, { status: EscrowStatus.CANCELLED }); - - await this.logEvent( - id, - EscrowEventType.CANCELLED, - userId, - { reason: dto.reason, previousStatus: escrow.status }, - ipAddress, - ); - await this.webhookService.dispatchEvent('escrow.cancelled', { - escrowId: id, - }); - - return this.findOne(id); - } - - async expire( - id: string, - dto: ExpireEscrowDto, - userId: string, - ipAddress?: string, - ): Promise { - const escrow = await this.findOne(id); - - const isAdmin = await this.isUserAdmin(userId); - if (escrow.creatorId !== userId && !isAdmin) { - throw new ForbiddenException( - 'Only the creator or an admin can expire this escrow', - ); - } - - return this.expireEscrow(escrow, { - actorId: userId, - ipAddress, - reason: dto.reason ?? 'Manually expired', - webhookReason: dto.reason ?? null, - }); - } - - async expireBySystem(id: string, reason: string): Promise { - const escrow = await this.findOne(id); - return this.expireEscrow(escrow, { - reason, - webhookReason: reason, - }); - } - - async fund( - id: string, - dto: FundEscrowDto, - userId: string, - walletAddress: string, - ipAddress?: string, - ): Promise { - const escrow = await this.findOne(id); - - if (escrow.creatorId !== userId) { - throw new ForbiddenException('Only the buyer can fund this escrow'); - } - - if (escrow.status !== EscrowStatus.PENDING) { - throw new BadRequestException( - 'Escrow can only be funded while in pending status', - ); - } - - if (escrow.stellarTxHash) { - throw new BadRequestException('Escrow is already funded'); - } - - const unacceptedRequired = (escrow.parties ?? []).filter( - (p) => - (p.role === PartyRole.BUYER || p.role === PartyRole.SELLER) && - p.status !== PartyStatus.ACCEPTED, - ); - if (unacceptedRequired.length > 0) { - throw new BadRequestException( - 'All buyer and seller parties must accept their invitation before the escrow can be funded', - ); - } - - const escrowAmount = Number(escrow.amount); - if (Number(dto.amount) !== escrowAmount) { - throw new BadRequestException('Amount must match the escrow amount'); - } - - validateTransition(escrow.status, EscrowStatus.ACTIVE); - - const stellarTxHash = - await this.stellarIntegrationService.fundOnChainEscrow( - id, - walletAddress, - String(dto.amount), - escrow.assetCode ?? 'XLM', - ); - - const fundedAt = new Date(); - await this.escrowRepository.update(id, { - stellarTxHash, - fundedAt, - status: EscrowStatus.ACTIVE, - }); - - await this.logEvent( - id, - EscrowEventType.FUNDED, - userId, - { stellarTxHash }, - ipAddress, - ); - await this.webhookService.dispatchEvent('escrow.funded', { - escrowId: id, - stellarTxHash, - }); - - return this.findOne(id); - } - - async isUserPartyToEscrow( - escrowId: string, - userId: string, - ): Promise { - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - relations: ['parties'], - }); - - if (!escrow) { - return false; - } - - if (escrow.creatorId === userId) { - return true; - } - - return escrow.parties?.some((party) => party.userId === userId) ?? false; - } - - async releaseEscrow( - escrowId: string, - currentUserId: string, - manual = false, - ): Promise { - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - relations: ['conditions', 'milestones'], - }); - - if (!escrow) { - throw new NotFoundException('Escrow not found'); - } - - // Idempotency protection - if (escrow.status === EscrowStatus.COMPLETED || escrow.isReleased) { - return escrow; // Safe no-op - } - - if (escrow.status !== EscrowStatus.ACTIVE) { - throw new BadRequestException('Escrow not active'); - } - - // Prevent operations on expired escrows - if (escrow.expiresAt && escrow.expiresAt < new Date()) { - throw new BadRequestException( - 'Cannot release an expired escrow. Use expire endpoint instead.', - ); - } - - // Manual release must be buyer - if (manual && escrow.creatorId !== currentUserId) { - throw new ForbiddenException('Only buyer can release escrow'); - } - - // Auto release validation - if (!manual) { - const allConditionsConfirmed = escrow.conditions.every( - (c) => c.isMet === true, - ); - - if (!allConditionsConfirmed) { - throw new BadRequestException( - 'All conditions must be confirmed for auto-release', - ); - } - } - - // 🔹 Execute on-chain transfer - const txHash = await this.stellarIntegrationService.completeOnChainEscrow( - escrow.id, - escrow.creatorId, - ); - - escrow.status = EscrowStatus.COMPLETED; - escrow.isReleased = true; - escrow.releaseTransactionHash = txHash; - - await this.escrowRepository.save(escrow); - - await this.logEvent(escrow.id, EscrowEventType.COMPLETED, currentUserId, { - txHash, - }); - await this.webhookService.dispatchEvent('escrow.released', { - escrowId: escrow.id, - txHash, - }); - - return escrow; - } - - async fulfillCondition( - escrowId: string, - conditionId: string, - dto: FulfillConditionDto, - userId: string, - ipAddress?: string, - ): Promise { - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - relations: ['parties', 'conditions'], - }); - - if (!escrow) { - throw new NotFoundException('Escrow not found'); - } - - if (escrow.status !== EscrowStatus.ACTIVE) { - throw new BadRequestException( - 'Escrow must be active to fulfill conditions', - ); - } - - // Prevent operations on expired escrows - if (escrow.expiresAt && escrow.expiresAt < new Date()) { - throw new BadRequestException( - 'Cannot fulfill conditions on an expired escrow', - ); - } - - // Check if user is a seller - const sellerParty = escrow.parties?.find( - (p) => p.role === PartyRole.SELLER && p.userId === userId, - ); - - if (!sellerParty) { - throw new ForbiddenException('Only sellers can fulfill conditions'); - } - - const condition = await this.conditionRepository.findOne({ - where: { id: conditionId, escrowId }, - relations: ['escrow'], - }); - - if (!condition) { - throw new NotFoundException('Condition not found'); - } - - if (condition.isFulfilled) { - return condition; // idempotent - } - - // Mark condition as fulfilled by seller - condition.isFulfilled = true; - condition.fulfilledAt = new Date(); - condition.fulfilledByUserId = userId; - condition.fulfillmentNotes = dto.notes; - condition.fulfillmentEvidence = dto.evidence; - - await this.conditionRepository.save(condition); - - await this.logEvent( - escrowId, - EscrowEventType.CONDITION_FULFILLED, - userId, - { - conditionId, - notes: dto.notes, - evidence: dto.evidence, - }, - ipAddress, - ); - - // Dispatch webhook for condition fulfillment - await this.webhookService.dispatchEvent('condition.fulfilled', { - escrowId, - conditionId, - fulfilledBy: userId, - }); - - return condition; - } - - async confirmCondition( - escrowId: string, - conditionId: string, - userId: string, - ipAddress?: string, - ): Promise { - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - relations: ['parties', 'conditions'], - }); - - if (!escrow) { - throw new NotFoundException('Escrow not found'); - } - - if (escrow.status !== EscrowStatus.ACTIVE) { - throw new BadRequestException( - 'Escrow must be active to confirm conditions', - ); - } - - // Prevent operations on expired escrows - if (escrow.expiresAt && escrow.expiresAt < new Date()) { - throw new BadRequestException( - 'Cannot confirm conditions on an expired escrow', - ); - } - - // Check if user is a buyer - const buyerParty = escrow.parties?.find( - (p) => p.role === PartyRole.BUYER && p.userId === userId, - ); - - if (!buyerParty) { - throw new ForbiddenException('Only buyers can confirm conditions'); - } - - const condition = await this.conditionRepository.findOne({ - where: { id: conditionId, escrowId }, - relations: ['escrow', 'escrow.conditions'], - }); - - if (!condition) { - throw new NotFoundException('Condition not found'); - } - - if (!condition.isFulfilled) { - throw new BadRequestException( - 'Condition must be fulfilled before it can be confirmed', - ); - } - - if (condition.isMet) { - return condition; // idempotent - } - - // Mark condition as confirmed by buyer - condition.isMet = true; - condition.metAt = new Date(); - condition.metByUserId = userId; - - await this.conditionRepository.save(condition); - - await this.logEvent( - escrowId, - EscrowEventType.CONDITION_MET, - userId, - { - conditionId, - confirmedBy: userId, - }, - ipAddress, - ); - - // Check if all conditions are now met for auto-release - const allConditionsMet = escrow.conditions.every((c) => - c.id === condition.id ? true : c.isMet, - ); - - if (allConditionsMet) { - await this.releaseEscrow( - escrow.id, - escrow.creatorId, - false, // auto release - ); - } - - // Dispatch webhook for condition confirmation - await this.webhookService.dispatchEvent('condition.confirmed', { - escrowId, - conditionId, - confirmedBy: userId, - allConditionsMet, - }); - - return condition; - } - - async findEvents( - userId: string, - query: ListEventsDto, - escrowId?: string, - ): Promise<{ - data: EventResponseDto[]; - total: number; - page: number; - limit: number; - }> { - const page = query.page || 1; - const limit = query.limit || 10; - const skip = (page - 1) * limit; - - const qb: SelectQueryBuilder = this.eventRepository - .createQueryBuilder('event') - .leftJoinAndSelect('event.escrow', 'escrow') - .leftJoinAndSelect('escrow.parties', 'party') - .leftJoinAndSelect('escrow.creator', 'creator') - .where('(escrow.creatorId = :userId OR party.userId = :userId)', { - userId, - }); - - // Apply escrowId filter if provided (either from parameter or query) - const effectiveEscrowId = escrowId || query.escrowId; - if (effectiveEscrowId) { - qb.andWhere('event.escrowId = :escrowId', { - escrowId: effectiveEscrowId, - }); - } - - if (query.eventType) { - qb.andWhere('event.eventType = :eventType', { - eventType: query.eventType, - }); - } - - if (query.actorId) { - qb.andWhere('event.actorId = :actorId', { actorId: query.actorId }); - } - - if (query.dateFrom) { - qb.andWhere('event.createdAt >= :dateFrom', { - dateFrom: new Date(query.dateFrom), - }); - } - - if (query.dateTo) { - qb.andWhere('event.createdAt <= :dateTo', { - dateTo: new Date(query.dateTo), - }); - } - - const sortOrder = query.sortOrder === EventSortOrder.ASC ? 'ASC' : 'DESC'; - qb.orderBy(`event.${query.sortBy || 'createdAt'}`, sortOrder); - - const [events, total] = await qb.skip(skip).take(limit).getManyAndCount(); - - // Transform to response DTO - const data: EventResponseDto[] = events.map((event) => ({ - id: event.id, - escrowId: event.escrowId, - eventType: event.eventType, - actorId: event.actorId, - data: event.data, - ipAddress: event.ipAddress, - createdAt: event.createdAt, - cursor: event.cursor, - escrow: event.escrow - ? { - id: event.escrow.id, - title: event.escrow.title, - amount: event.escrow.amount, - assetCode: event.escrow.assetCode, - assetIssuer: event.escrow.assetIssuer, - status: event.escrow.status, - } - : undefined, - actor: event.actorId - ? { - walletAddress: event.actorId, // In real implementation, this would come from user lookup - } - : undefined, - })); - - return { data, total, page, limit }; - } - - async fileDispute( - escrowId: string, - userId: string, - dto: FileDisputeDto, - ipAddress?: string, - ): Promise { - const escrow = await this.findOne(escrowId); - - const existing = await this.disputeRepository.findOne({ - where: { escrowId }, - }); - if (existing) { - throw new ConflictException( - 'A dispute has already been filed for this escrow', - ); - } - - if (escrow.status !== EscrowStatus.ACTIVE) { - throw new BadRequestException( - 'Disputes can only be filed against active escrows', - ); - } - - const isBuyer = escrow.creatorId === userId; - const filingParty = escrow.parties?.find( - (p) => p.userId === userId && p.role !== PartyRole.ARBITRATOR, - ); - if (!isBuyer && !filingParty) { - throw new ForbiddenException( - 'Only a buyer or seller party can file a dispute', - ); - } - - validateTransition(escrow.status, EscrowStatus.DISPUTED); - await this.escrowRepository.update(escrowId, { - status: EscrowStatus.DISPUTED, - }); - - const dispute = this.disputeRepository.create({ - escrowId, - filedByUserId: userId, - reason: dto.reason, - evidence: dto.evidence ?? null, - status: DisputeStatus.OPEN, - }); - const savedDispute = await this.disputeRepository.save(dispute); - - await this.logEvent( - escrowId, - EscrowEventType.DISPUTE_FILED, - userId, - { disputeId: savedDispute.id, reason: dto.reason }, - ipAddress, - ); - - await this.webhookService.dispatchEvent('escrow.disputed', { - escrowId, - disputeId: savedDispute.id, - }); - - return this.disputeRepository.findOne({ - where: { id: savedDispute.id }, - relations: ['filedBy'], - }) as Promise; - } - - async getDispute(escrowId: string): Promise { - // Caller access is already enforced by EscrowAccessGuard at the controller layer - const dispute = await this.disputeRepository.findOne({ - where: { escrowId }, - relations: ['filedBy', 'resolvedBy'], - }); - - if (!dispute) { - throw new NotFoundException('No dispute found for this escrow'); - } - - return dispute; - } - - async resolveDispute( - escrowId: string, - arbitratorUserId: string, - dto: ResolveDisputeDto, - ipAddress?: string, - ): Promise { - const escrow = await this.findOne(escrowId); - - if (escrow.status !== EscrowStatus.DISPUTED) { - throw new BadRequestException('This escrow is not currently disputed'); - } - - // Caller must be an arbitrator party on this escrow - const isArbitrator = escrow.parties?.some( - (p) => p.userId === arbitratorUserId && p.role === PartyRole.ARBITRATOR, - ); - if (!isArbitrator) { - throw new ForbiddenException( - 'Only an assigned arbitrator can resolve a dispute', - ); - } - - const dispute = await this.getDispute(escrowId); - - if (dispute.status === DisputeStatus.RESOLVED) { - throw new ConflictException('This dispute has already been resolved'); - } - - // For a split outcome both percentages are required and must sum to 100 - if (dto.outcome === DisputeOutcome.SPLIT) { - if (dto.sellerPercent === undefined || dto.buyerPercent === undefined) { - throw new UnprocessableEntityException( - 'sellerPercent and buyerPercent are required for a split outcome', - ); - } - if (dto.sellerPercent + dto.buyerPercent !== 100) { - throw new UnprocessableEntityException( - 'sellerPercent and buyerPercent must sum to 100', - ); - } - } - - // Determine the new escrow status based on the resolution outcome - const nextEscrowStatus = - dto.outcome === DisputeOutcome.REFUNDED_TO_BUYER - ? EscrowStatus.CANCELLED - : EscrowStatus.COMPLETED; - - validateTransition(escrow.status, nextEscrowStatus); - await this.escrowRepository.update(escrowId, { status: nextEscrowStatus }); - - dispute.status = DisputeStatus.RESOLVED; - dispute.resolvedByUserId = arbitratorUserId; - dispute.resolutionNotes = dto.resolutionNotes; - dispute.outcome = dto.outcome; - dispute.sellerPercent = dto.sellerPercent ?? null; - dispute.buyerPercent = dto.buyerPercent ?? null; - dispute.resolvedAt = new Date(); - - const resolved = await this.disputeRepository.save(dispute); - - await this.logEvent( - escrowId, - EscrowEventType.DISPUTE_RESOLVED, - arbitratorUserId, - { - disputeId: resolved.id, - outcome: dto.outcome, - sellerPercent: dto.sellerPercent, - buyerPercent: dto.buyerPercent, - nextEscrowStatus, - }, - ipAddress, - ); - - await this.webhookService.dispatchEvent('escrow.resolved', { - escrowId, - disputeId: resolved.id, - outcome: dto.outcome, - }); - - return this.disputeRepository.findOne({ - where: { id: resolved.id }, - relations: ['filedBy', 'resolvedBy'], - }) as Promise; - } - - async proposeMilestoneChange( - escrowId: string, - conditionId: string, - dto: ProposeMilestoneChangeDto, - userId: string, - ): Promise { - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - relations: ['conditions', 'parties'], - }); - - if (!escrow) throw new NotFoundException('Escrow not found'); - - if (escrow.status !== EscrowStatus.ACTIVE) { - throw new BadRequestException( - 'Milestones can only be changed when the escrow is ACTIVE', - ); - } - - const isBuyer = - escrow.creatorId === userId || - escrow.parties.some( - (p) => p.userId === userId && p.role === PartyRole.BUYER, - ); - const isSeller = escrow.parties.some( - (p) => p.userId === userId && p.role === PartyRole.SELLER, - ); - if (!isBuyer && !isSeller) { - throw new ForbiddenException( - 'Only the buyer or seller can propose milestone changes', - ); - } - - const condition = escrow.conditions.find((c) => c.id === conditionId); - if (!condition) throw new NotFoundException('Condition not found'); - - if (condition.isFulfilled || condition.isMet) { - throw new BadRequestException( - 'Cannot change a milestone that is already fulfilled or met', - ); - } - - if (dto.amount === undefined && dto.description === undefined) { - throw new BadRequestException( - 'Must propose at least one change (amount or description)', - ); - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - condition.proposedAmount = (dto.amount ?? null) as any; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - condition.proposedDescription = (dto.description ?? null) as any; - condition.proposedByUserId = userId; - - await this.conditionRepository.save(condition); - return condition; - } - - async acceptMilestoneChange( - escrowId: string, - conditionId: string, - userId: string, - ): Promise { - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - relations: ['conditions', 'parties'], - }); - - if (!escrow) throw new NotFoundException('Escrow not found'); - - if (escrow.status !== EscrowStatus.ACTIVE) { - throw new BadRequestException( - 'Milestones can only be changed when the escrow is ACTIVE', - ); - } - - const isBuyer = - escrow.creatorId === userId || - escrow.parties.some( - (p) => p.userId === userId && p.role === PartyRole.BUYER, - ); - const isSeller = escrow.parties.some( - (p) => p.userId === userId && p.role === PartyRole.SELLER, - ); - if (!isBuyer && !isSeller) { - throw new ForbiddenException( - 'Only the buyer or seller can accept milestone changes', - ); - } - - const condition = escrow.conditions.find((c) => c.id === conditionId); - if (!condition) throw new NotFoundException('Condition not found'); - - if (!condition.proposedByUserId) { - throw new BadRequestException('No pending proposal for this milestone'); - } - - if (condition.proposedByUserId === userId) { - throw new ForbiddenException( - 'You cannot accept your own proposed changes', - ); - } - - if ( - condition.proposedAmount !== null && - condition.proposedAmount !== undefined - ) { - condition.amount = condition.proposedAmount; - } - if (condition.proposedDescription) { - condition.description = condition.proposedDescription; - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - condition.proposedAmount = null as any; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - condition.proposedDescription = null as any; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - condition.proposedByUserId = null as any; - - await this.conditionRepository.save(condition); - return condition; - } - - async acceptPartyInvitation( - escrowId: string, - partyId: string, - userId: string, - ipAddress?: string, - ): Promise { - const party = await this.partyRepository.findOne({ - where: { id: partyId, escrowId }, - relations: ['escrow'], - }); - - if (!party) throw new NotFoundException('Party invitation not found'); - if (party.userId !== userId) - throw new ForbiddenException( - 'You can only respond to your own invitation', - ); - if (party.status !== PartyStatus.PENDING) - throw new BadRequestException(`Invitation already ${party.status}`); - - party.status = PartyStatus.ACCEPTED; - party.respondedAt = new Date(); - await this.partyRepository.save(party); - - await this.logEvent( - escrowId, - EscrowEventType.PARTY_ACCEPTED, - userId, - { partyId }, - ipAddress, - ); - - const escrow = party.escrow; - if (escrow?.creatorId) { - const acceptedUser = await this.userRepository.findOne({ - where: { id: userId }, - }); - this.notificationService - .handleEscrowEvent( - escrow.creatorId, - NotificationEventType.PARTY_ACCEPTED, - { - escrowId, - escrowTitle: escrow.title, - role: party.role, - acceptedByUserId: userId, - email: acceptedUser?.email ?? undefined, - }, - ) - .catch(() => undefined); - } - - return party; - } - - async rejectPartyInvitation( - escrowId: string, - partyId: string, - userId: string, - ipAddress?: string, - ): Promise { - const party = await this.partyRepository.findOne({ - where: { id: partyId, escrowId }, - relations: ['escrow'], - }); - - if (!party) throw new NotFoundException('Party invitation not found'); - if (party.userId !== userId) - throw new ForbiddenException( - 'You can only respond to your own invitation', - ); - if (party.status !== PartyStatus.PENDING) - throw new BadRequestException(`Invitation already ${party.status}`); - - party.status = PartyStatus.REJECTED; - party.respondedAt = new Date(); - await this.partyRepository.save(party); - - await this.logEvent( - escrowId, - EscrowEventType.PARTY_REJECTED, - userId, - { partyId }, - ipAddress, - ); - - const escrow = party.escrow; - if (escrow) { - const rejectedUser = await this.userRepository.findOne({ - where: { id: userId }, - }); - - if (escrow.creatorId) { - this.notificationService - .handleEscrowEvent( - escrow.creatorId, - NotificationEventType.PARTY_REJECTED, - { - escrowId, - escrowTitle: escrow.title, - role: party.role, - rejectedByUserId: userId, - email: rejectedUser?.email ?? undefined, - }, - ) - .catch(() => undefined); - } - - // Auto-cancel when a required party (buyer or seller) rejects a PENDING escrow - const isRequired = - party.role === PartyRole.BUYER || party.role === PartyRole.SELLER; - if (isRequired && escrow.status === EscrowStatus.PENDING) { - await this.escrowRepository.update(escrowId, { - status: EscrowStatus.CANCELLED, - }); - await this.logEvent( - escrowId, - EscrowEventType.CANCELLED, - userId, - { reason: `Required party (${party.role}) rejected the invitation` }, - ipAddress, - ); - await this.webhookService.dispatchEvent('escrow.cancelled', { - escrowId, - }); - } - } - - return party; - } - - async getPendingInvitations(userId: string): Promise { - return this.partyRepository.find({ - where: { userId, status: PartyStatus.PENDING }, - relations: ['escrow', 'escrow.parties', 'escrow.creator'], - }); - } - - private async logEvent( - escrowId: string, - eventType: EscrowEventType, - actorId?: string, - data?: Record, - ipAddress?: string, - ): Promise { - const event = this.eventRepository.create({ - escrowId, - eventType, - actorId, - data, - ipAddress, - }); - - return this.eventRepository.save(event); - } - - async isUserAdmin(userId: string): Promise { - const user = await this.userRepository.findOne({ - where: { id: userId }, - }); - - return user?.role === UserRole.ADMIN || user?.role === UserRole.SUPER_ADMIN; - } - - private async expireEscrow( - escrow: Escrow, - options: { - actorId?: string; - ipAddress?: string; - reason: string; - webhookReason: string | null; - }, - ): Promise { - if (isTerminalStatus(escrow.status)) { - throw new BadRequestException( - `Cannot expire an escrow that is already ${escrow.status}`, - ); - } - - if ( - escrow.status !== EscrowStatus.PENDING && - escrow.status !== EscrowStatus.ACTIVE - ) { - throw new BadRequestException( - 'Escrow can only be expired while in pending or active status', - ); - } - - validateTransition(escrow.status, EscrowStatus.EXPIRED); - - await this.escrowRepository.update(escrow.id, { - status: EscrowStatus.EXPIRED, - isActive: false, - }); - - await this.logEvent( - escrow.id, - EscrowEventType.EXPIRED, - options.actorId, - { - reason: options.reason, - previousStatus: escrow.status, - }, - options.ipAddress, - ); - - await this.webhookService.dispatchEvent('escrow.expired', { - escrowId: escrow.id, - reason: options.webhookReason, - }); - - return this.findOne(escrow.id); - } - - async releaseMilestone( - escrowId: string, - conditionId: string, - userId: string, - ): Promise { - const escrow = await this.findOne(escrowId); - - if (escrow.type !== EscrowType.MILESTONE) { - throw new BadRequestException( - 'Only milestone escrows support partial releases', - ); - } - - if (escrow.status !== EscrowStatus.ACTIVE) { - throw new BadRequestException('Escrow is not active'); - } - - if (escrow.expiresAt && escrow.expiresAt < new Date()) { - throw new BadRequestException('Escrow has expired'); - } - - // Check if user is depositor or arbitrator - const isDepositor = escrow.creatorId === userId; - const isArbitrator = escrow.parties.some( - (party) => party.userId === userId && party.role === PartyRole.ARBITRATOR, - ); - - if (!isDepositor && !isArbitrator) { - throw new ForbiddenException( - 'Only depositor or arbitrator can release a milestone', - ); - } - - // Find the condition - const condition = escrow.conditions.find((c) => c.id === conditionId); - if (!condition) { - throw new NotFoundException('Condition not found'); - } - - if (condition.isReleased) { - throw new BadRequestException('This milestone has already been released'); - } - - if (!condition.isMet) { - throw new BadRequestException( - 'Milestone must be confirmed before releasing', - ); - } - - if (!condition.amount) { - throw new BadRequestException('Milestone has no amount defined'); - } - - // Get the seller/recipient - const seller = escrow.parties.find((p) => p.role === PartyRole.SELLER); - if (!seller) { - throw new BadRequestException('No seller found for this escrow'); - } - - // Calculate released amount - const releaseAmount = parseFloat(condition.amount.toString()); - const newReleasedAmount = - parseFloat(escrow.releasedAmount.toString()) + releaseAmount; - - // Update escrow - escrow.releasedAmount = newReleasedAmount; - - // Check if all milestones are released - const totalMilestonesAmount = escrow.conditions.reduce( - (sum, c) => sum + (c.amount ? parseFloat(c.amount.toString()) : 0), - 0, - ); - - // If all are released, set escrow to completed - if ( - newReleasedAmount >= parseFloat(escrow.amount.toString()) || - newReleasedAmount >= totalMilestonesAmount - ) { - escrow.status = EscrowStatus.COMPLETED; - escrow.isReleased = true; - } - - // Mark condition as released - condition.isReleased = true; - condition.releasedAt = new Date(); - - // Save changes - await this.escrowRepository.save(escrow); - await this.conditionRepository.save(condition); - - // Log the event - await this.logEvent(escrowId, EscrowEventType.MILESTONE_RELEASED, userId, { - conditionId, - amount: releaseAmount, - }); - - // Dispatch webhook - await this.webhookService.dispatchEvent('escrow.milestone_released', { - escrowId, - conditionId, - amount: releaseAmount, - }); - - return this.findOne(escrowId); - } - - async uploadEvidence( - escrowId: string, - userId: string, - file: { buffer: Buffer; originalname: string }, - ): Promise<{ cid: string; url: string }> { - const escrow = await this.findOne(escrowId); - - if (escrow.status !== EscrowStatus.DISPUTED) { - throw new BadRequestException('Escrow is not in disputed status'); - } - - const dispute = await this.disputeRepository.findOne({ - where: { escrowId }, - }); - - if (!dispute) { - throw new NotFoundException('Dispute record not found'); - } - - // Upload to IPFS - const cid = await this.ipfsService.uploadFile( - file.buffer, - file.originalname, - ); - - // Update dispute evidence list - const evidence = dispute.evidence || []; - evidence.push(cid); - dispute.evidence = evidence; - - await this.disputeRepository.save(dispute); - - return { - cid, - url: this.ipfsService.getGatewayUrl(cid), - }; - } -} diff --git a/apps/backend/src/modules/escrow/utils/emergency-pause.util.ts b/apps/backend/src/modules/escrow/utils/emergency-pause.util.ts deleted file mode 100644 index 2e655a07..00000000 --- a/apps/backend/src/modules/escrow/utils/emergency-pause.util.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Emergency pause circuit breaker. - * Tracks pause state in-memory and exposes helpers to pause, resume, - * and check whether operations are currently allowed. - */ - -let paused = false; -let pausedAt: Date | null = null; -let pausedBy: string | null = null; - -export interface PauseState { - paused: boolean; - pausedAt: Date | null; - pausedBy: string | null; -} - -/** Activates the circuit breaker, blocking all guarded operations. */ -export function activatePause(adminId: string): void { - paused = true; - pausedAt = new Date(); - pausedBy = adminId; -} - -/** Deactivates the circuit breaker, resuming normal operations. */ -export function deactivatePause(): void { - paused = false; - pausedAt = null; - pausedBy = null; -} - -/** Returns the current pause state. */ -export function getPauseState(): PauseState { - return { paused, pausedAt, pausedBy }; -} - -/** - * Throws an error if the system is currently paused. - * Use this guard at the start of any sensitive operation. - */ -export function assertNotPaused(): void { - if (paused) { - throw new Error( - `System is paused (since ${pausedAt?.toISOString()}, by ${pausedBy})`, - ); - } -} diff --git a/apps/backend/src/modules/escrow/utils/metadata-hash.util.spec.ts b/apps/backend/src/modules/escrow/utils/metadata-hash.util.spec.ts deleted file mode 100644 index d90bf3ec..00000000 --- a/apps/backend/src/modules/escrow/utils/metadata-hash.util.spec.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { normalizeMetadataHash } from './metadata-hash.util'; - -const BASE32_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567'; -const BASE58BTC_ALPHABET = - '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; - -describe('normalizeMetadataHash', () => { - const digest = Uint8Array.from( - Array.from({ length: 32 }, (_, index) => index + 1), - ); - const digestHex = Array.from(digest, (byte) => - byte.toString(16).padStart(2, '0'), - ).join(''); - - it('normalizes raw hex digests', () => { - expect(normalizeMetadataHash(digestHex.toUpperCase())).toBe(digestHex); - }); - - it('normalizes cidv1 base32 references', () => { - const cid = `b${encodeBase32( - Uint8Array.from([0x01, 0x55, 0x12, 0x20, ...digest]), - )}`; - - expect(normalizeMetadataHash(`ipfs://${cid}`)).toBe(digestHex); - }); - - it('normalizes cidv0 base58 references', () => { - const cid = encodeBase58(Uint8Array.from([0x12, 0x20, ...digest])); - - expect(normalizeMetadataHash(cid)).toBe(digestHex); - }); - - it('rejects all-zero raw hex digests', () => { - expect(() => normalizeMetadataHash('0'.repeat(64))).toThrow('zeroes'); - }); - - it('rejects all-zero cid digests', () => { - const cid = `b${encodeBase32( - Uint8Array.from([0x01, 0x55, 0x12, 0x20, ...new Uint8Array(32)]), - )}`; - - expect(() => normalizeMetadataHash(cid)).toThrow('zeroes'); - }); - - it('rejects malformed raw hex digests', () => { - expect(() => normalizeMetadataHash(digestHex.slice(0, 62))).toThrow( - '64 hex characters', - ); - }); - - it('rejects non-sha256 multihashes', () => { - const cid = `b${encodeBase32( - Uint8Array.from([0x01, 0x55, 0x13, 0x20, ...digest]), - )}`; - - expect(() => normalizeMetadataHash(cid)).toThrow('sha2-256'); - }); -}); - -function encodeBase32(bytes: Uint8Array): string { - let bits = 0; - let bitCount = 0; - let output = ''; - - for (const byte of bytes) { - bits = (bits << 8) | byte; - bitCount += 8; - - while (bitCount >= 5) { - bitCount -= 5; - output += BASE32_ALPHABET[(bits >> bitCount) & 31]; - } - } - - if (bitCount > 0) { - output += BASE32_ALPHABET[(bits << (5 - bitCount)) & 31]; - } - - return output; -} - -function encodeBase58(bytes: Uint8Array): string { - if (bytes.length === 0) { - return ''; - } - - const digits = [0]; - for (const byte of bytes) { - let carry = byte; - for (let i = 0; i < digits.length; i += 1) { - const next = digits[i] * 256 + carry; - digits[i] = next % 58; - carry = Math.floor(next / 58); - } - - while (carry > 0) { - digits.push(carry % 58); - carry = Math.floor(carry / 58); - } - } - - let output = ''; - for (let i = 0; i < bytes.length && bytes[i] === 0; i += 1) { - output += BASE58BTC_ALPHABET[0]; - } - - for (let i = digits.length - 1; i >= 0; i -= 1) { - output += BASE58BTC_ALPHABET[digits[i]]; - } - - return output; -} diff --git a/apps/backend/src/modules/escrow/utils/metadata-hash.util.ts b/apps/backend/src/modules/escrow/utils/metadata-hash.util.ts deleted file mode 100644 index 8fcff776..00000000 --- a/apps/backend/src/modules/escrow/utils/metadata-hash.util.ts +++ /dev/null @@ -1,215 +0,0 @@ -const SHA256_MULTIHASH_CODE = 0x12; -const SHA256_DIGEST_LENGTH = 32; -const CID_V1 = 1; - -const BASE32_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567'; -const BASE58BTC_ALPHABET = - '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; -const HEX_32_RE = /^[0-9a-f]{64}$/; -const HEX_RE = /^[0-9a-f]+$/i; -const ZERO_DIGEST_HEX = '0'.repeat(SHA256_DIGEST_LENGTH * 2); - -export function normalizeMetadataHash(reference: string): string { - const value = sanitizeReference(reference); - const lowered = value.toLowerCase(); - - if (HEX_32_RE.test(lowered)) { - return validateDigestHex(lowered); - } - - if (HEX_RE.test(value)) { - throw new Error('metadata hash must be 64 hex characters'); - } - - const cid = extractCid(value); - const digest = cid.startsWith('Qm') - ? extractDigestFromCidV0(cid) - : extractDigestFromCidV1(cid); - - return validateDigestHex(bytesToHex(digest)); -} - -function sanitizeReference(reference: string): string { - if (!reference) { - throw new Error('metadata hash is required'); - } - const value = reference.trim(); - if (!value) { - throw new Error('metadata hash is required'); - } - - return value; -} - -function extractCid(reference: string): string { - if (!reference.toLowerCase().startsWith('ipfs://')) { - return reference; - } - - const withoutScheme = reference.slice('ipfs://'.length); - const cid = withoutScheme.split(/[/?#]/, 1)[0]; - if (!cid) { - throw new Error('invalid ipfs reference'); - } - - return cid; -} - -function extractDigestFromCidV0(cid: string): Uint8Array { - return parseMultihash(decodeBase58(cid)); -} - -function extractDigestFromCidV1(cid: string): Uint8Array { - const multibase = cid[0]; - const payload = cid.slice(1); - - if (!payload) { - throw new Error('invalid cid'); - } - - let decoded: Uint8Array; - if (multibase === 'b' || multibase === 'B') { - decoded = decodeBase32(payload.toLowerCase()); - } else if (multibase === 'z') { - decoded = decodeBase58(payload); - } else { - throw new Error('unsupported cid multibase'); - } - - let offset = 0; - const version = readVarint(decoded, offset); - offset = version.nextOffset; - if (version.value !== CID_V1) { - throw new Error('unsupported cid version'); - } - - const codec = readVarint(decoded, offset); - offset = codec.nextOffset; - void codec; - - return parseMultihash(decoded.slice(offset)); -} - -function parseMultihash(bytes: Uint8Array): Uint8Array { - let offset = 0; - const code = readVarint(bytes, offset); - offset = code.nextOffset; - const length = readVarint(bytes, offset); - offset = length.nextOffset; - - if (code.value !== SHA256_MULTIHASH_CODE) { - throw new Error('metadata hash must use sha2-256'); - } - - if (length.value !== SHA256_DIGEST_LENGTH) { - throw new Error('metadata hash digest must be 32 bytes'); - } - - const digest = bytes.slice(offset, offset + length.value); - if (digest.length !== SHA256_DIGEST_LENGTH) { - throw new Error('metadata hash digest is truncated'); - } - - if (offset + length.value !== bytes.length) { - throw new Error('invalid multihash length'); - } - - return digest; -} - -function readVarint( - bytes: Uint8Array, - offset: number, -): { value: number; nextOffset: number } { - let value = 0; - let shift = 0; - let index = offset; - - while (index < bytes.length) { - const current = bytes[index]; - value |= (current & 0x7f) << shift; - index += 1; - - if ((current & 0x80) === 0) { - return { value, nextOffset: index }; - } - - shift += 7; - if (shift > 28) { - throw new Error('varint is too large'); - } - } - - throw new Error('unexpected end of varint'); -} - -function decodeBase32(value: string): Uint8Array { - let bits = 0; - let bitCount = 0; - const bytes: number[] = []; - - for (const char of value) { - const index = BASE32_ALPHABET.indexOf(char); - if (index === -1) { - throw new Error('invalid base32 cid'); - } - - bits = (bits << 5) | index; - bitCount += 5; - - while (bitCount >= 8) { - bitCount -= 8; - bytes.push((bits >> bitCount) & 0xff); - } - } - - return Uint8Array.from(bytes); -} - -function decodeBase58(value: string): Uint8Array { - const bytes: number[] = [0]; - - for (const char of value) { - const carryIndex = BASE58BTC_ALPHABET.indexOf(char); - if (carryIndex === -1) { - throw new Error('invalid base58 cid'); - } - - let carry = carryIndex; - for (let i = 0; i < bytes.length; i += 1) { - const next = bytes[i] * 58 + carry; - bytes[i] = next & 0xff; - carry = next >> 8; - } - - while (carry > 0) { - bytes.push(carry & 0xff); - carry >>= 8; - } - } - - for ( - let i = 0; - i < value.length && value[i] === BASE58BTC_ALPHABET[0]; - i += 1 - ) { - bytes.push(0); - } - - bytes.reverse(); - return Uint8Array.from(bytes); -} - -function bytesToHex(bytes: Uint8Array): string { - return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join( - '', - ); -} - -function validateDigestHex(digestHex: string): string { - if (digestHex === ZERO_DIGEST_HEX) { - throw new Error('metadata hash cannot be all zeroes'); - } - - return digestHex; -} diff --git a/apps/backend/src/modules/health/health.controller.ts b/apps/backend/src/modules/health/health.controller.ts deleted file mode 100644 index d4c00272..00000000 --- a/apps/backend/src/modules/health/health.controller.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { TypeOrmHealthIndicator } from '@nestjs/terminus'; -import { EscrowGateway } from '../../gateways/escrow.gateway'; -import { Controller, Get } from '@nestjs/common'; -import { - HealthCheck, - HealthCheckService, - HealthCheckResult, - HealthIndicatorResult, -} from '@nestjs/terminus'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { User } from '../user/entities/user.entity'; -import { Escrow, EscrowStatus } from '../escrow/entities/escrow.entity'; -import { StellarService } from '../../services/stellar.service'; - -interface HealthInfo { - version: string; - nodeVersion: string; - uptime: number; - network: string; - databaseType: string; - metrics: { - activeEscrows: number; - totalUsers: number; - }; -} - -@Controller('health') -export class HealthController { - constructor( - private health: HealthCheckService, - private readonly typeOrmHealthIndicator: TypeOrmHealthIndicator, - private readonly stellarService: StellarService, - private readonly escrowGateway: EscrowGateway, - @InjectRepository(User) - private userRepository: Repository, - @InjectRepository(Escrow) - private escrowRepository: Repository, - ) {} - - @Get() - live(): { status: string } { - return { - status: 'ok', - }; - } - - @Get('ready') - @HealthCheck() - async ready(): Promise { - return this.health.check([ - () => this.checkDatabase(), - () => this.checkStellar(), - () => this.checkWebSocket(), - ]); - } - - private checkWebSocket(): HealthIndicatorResult { - return { - websocket: { - status: this.escrowGateway.isHealthy() ? 'up' : 'down', - }, - }; - } - - @Get('info') - async info(): Promise { - const activeEscrows = await this.escrowRepository.count({ - where: { status: EscrowStatus.ACTIVE }, - }); - const totalUsers = await this.userRepository.count(); - - return { - version: process.env.npm_package_version || '0.0.1', - nodeVersion: process.version, - uptime: process.uptime(), - network: process.env.STELLAR_NETWORK || 'testnet', - databaseType: 'sqlite', - metrics: { - activeEscrows, - totalUsers, - }, - }; - } - - private async checkDatabase(): Promise { - return this.typeOrmHealthIndicator.pingCheck('database'); - } - - private async checkStellar(): Promise { - const healthy = await this.stellarService.checkHealth(); - - return { - stellar: { - status: healthy ? 'up' : 'down', - }, - }; - } - - private checkMemory(): HealthIndicatorResult { - const heapUsedMB = process.memoryUsage().heapUsed / 1024 / 1024; - const thresholdMB = 512; - - if (heapUsedMB > thresholdMB) { - return { - memory: { - status: 'up', - warning: `Heap usage exceeded ${thresholdMB} MB`, - thresholdMB, - }, - }; - } - - return { - memory: { - status: 'up', - heapUsedMB: Number(heapUsedMB.toFixed(2)), - thresholdMB, - }, - }; - } - - private checkDisk(): HealthIndicatorResult { - // For simplicity, we'll just return up for now (real implementation would check disk space) - return { - disk: { - status: 'up', - }, - }; - } -} diff --git a/apps/backend/src/modules/health/health.module.ts b/apps/backend/src/modules/health/health.module.ts deleted file mode 100644 index 069362c9..00000000 --- a/apps/backend/src/modules/health/health.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Module } from '@nestjs/common'; -import { GatewaysModule } from '../../gateways/gateways.module'; -import { TerminusModule, TypeOrmHealthIndicator } from '@nestjs/terminus'; -import { HealthController } from './health.controller'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { User } from '../user/entities/user.entity'; -import { Escrow } from '../escrow/entities/escrow.entity'; - -@Module({ - imports: [ - TerminusModule, - TypeOrmModule.forFeature([User, Escrow]), - GatewaysModule, - ], - controllers: [HealthController], - providers: [TypeOrmHealthIndicator], -}) -export class HealthModule {} diff --git a/apps/backend/src/modules/ipfs/ipfs.controller.ts b/apps/backend/src/modules/ipfs/ipfs.controller.ts deleted file mode 100644 index bfba4066..00000000 --- a/apps/backend/src/modules/ipfs/ipfs.controller.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Controller, Get, Param, Redirect } from '@nestjs/common'; -import { IpfsService } from './ipfs.service'; - -@Controller('ipfs') -export class IpfsController { - constructor(private readonly ipfsService: IpfsService) {} - - @Get(':cid') - @Redirect() - getFile(@Param('cid') cid: string) { - const url = this.ipfsService.getGatewayUrl(cid); - return { url, statusCode: 302 }; - } -} diff --git a/apps/backend/src/modules/ipfs/ipfs.module.ts b/apps/backend/src/modules/ipfs/ipfs.module.ts deleted file mode 100644 index 2f017935..00000000 --- a/apps/backend/src/modules/ipfs/ipfs.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { ConfigModule } from '@nestjs/config'; -import { IpfsService } from './ipfs.service'; -import { IpfsController } from './ipfs.controller'; -import { IpfsProviderService } from './services/ipfs-provider.service'; -import ipfsConfig from '../../config/ipfs.config'; -import { Escrow } from '../escrow/entities/escrow.entity'; - -@Module({ - imports: [ - TypeOrmModule.forFeature([Escrow]), - ConfigModule.forFeature(ipfsConfig), - ], - providers: [IpfsService, IpfsProviderService], - controllers: [IpfsController], - exports: [IpfsService, IpfsProviderService], -}) -export class IpfsModule {} diff --git a/apps/backend/src/modules/ipfs/ipfs.service.spec.ts b/apps/backend/src/modules/ipfs/ipfs.service.spec.ts deleted file mode 100644 index de400f4e..00000000 --- a/apps/backend/src/modules/ipfs/ipfs.service.spec.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { IpfsService } from './ipfs.service'; -import { IpfsProviderService } from './services/ipfs-provider.service'; -import { Escrow } from '../escrow/entities/escrow.entity'; -import { computeMetadataHash } from './utils/metadata-hash.util'; -import ipfsConfig from '../../config/ipfs.config'; - -describe('IpfsService', () => { - let service: IpfsService; - let providerService: IpfsProviderService; - let escrowRepository: Repository; - - const mockEscrowRepository = { - findOne: jest.fn(), - save: jest.fn(), - }; - - const mockProviderService = { - pinJson: jest.fn(), - getJson: jest.fn(), - }; - - const mockConfig = { - provider: 'pinata', - pinataJwt: 'test-jwt', - gatewayUrl: 'https://gateway.pinata.cloud/ipfs/', - localNodeUrl: 'http://localhost:5001', - maxRetries: 1, - }; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - IpfsService, - { - provide: getRepositoryToken(Escrow), - useValue: mockEscrowRepository, - }, - { - provide: IpfsProviderService, - useValue: mockProviderService, - }, - { - provide: ipfsConfig.KEY, - useValue: mockConfig, - }, - ], - }).compile(); - - service = module.get(IpfsService); - providerService = module.get(IpfsProviderService); - escrowRepository = module.get>( - getRepositoryToken(Escrow), - ); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); - - describe('pinMetadata', () => { - it('should pin metadata to IPFS and update escrow', async () => { - const escrowId = 'test-escrow-id'; - const mockEscrow = { - id: escrowId, - parties: [ - { role: 'buyer', userId: 'buyer-1' }, - { role: 'seller', userId: 'seller-1' }, - ], - conditions: [ - { description: 'Condition 1', type: 'delivery', isFulfilled: false }, - ], - amount: 100, - assetCode: 'XLM', - expiresAt: new Date('2026-12-31'), - status: 'active', - ipfsVersion: 0, - }; - - mockEscrowRepository.findOne.mockResolvedValue(mockEscrow); - mockProviderService.pinJson.mockResolvedValue('test-cid'); - mockEscrowRepository.save.mockResolvedValue({ - ...mockEscrow, - ipfsCid: 'test-cid', - ipfsMetadataHash: 'test-hash', - ipfsVersion: 1, - }); - - const result = await service.pinMetadata(escrowId, {}); - - expect(result).toEqual({ - cid: 'test-cid', - metadataHash: expect.any(String), - }); - expect(mockProviderService.pinJson).toHaveBeenCalled(); - expect(mockEscrowRepository.save).toHaveBeenCalled(); - }); - - it('should throw error when escrow not found', async () => { - mockEscrowRepository.findOne.mockResolvedValue(null); - - await expect(service.pinMetadata('non-existent', {})).rejects.toThrow( - 'IPFS metadata pinning failed', - ); - }); - }); - - describe('getMetadata', () => { - it('should retrieve metadata from IPFS', async () => { - const escrowId = 'test-escrow-id'; - const mockEscrow = { - id: escrowId, - ipfsCid: 'test-cid', - }; - - const mockMetadata = { - escrowId, - buyer: 'buyer-1', - seller: 'seller-1', - amount: '100', - asset: 'XLM', - conditions: [], - deadline: '2026-12-31', - status: 'active', - timestamp: new Date().toISOString(), - version: 1, - }; - - mockEscrowRepository.findOne.mockResolvedValue(mockEscrow); - mockProviderService.getJson.mockResolvedValue(mockMetadata); - - const result = await service.getMetadata(escrowId); - - expect(result).toEqual(mockMetadata); - expect(mockProviderService.getJson).toHaveBeenCalledWith('test-cid'); - }); - - it('should throw error when no IPFS metadata found', async () => { - mockEscrowRepository.findOne.mockResolvedValue({ - id: 'test-escrow-id', - ipfsCid: null, - }); - - await expect(service.getMetadata('test-escrow-id')).rejects.toThrow( - 'Failed to retrieve IPFS metadata', - ); - }); - }); - - describe('verifyMetadata', () => { - it('should verify metadata hash is valid', async () => { - const escrowId = 'test-escrow-id'; - const mockMetadata = { - escrowId, - buyer: 'buyer-1', - seller: 'seller-1', - amount: '100', - asset: 'XLM', - conditions: [], - deadline: '2026-12-31', - status: 'active', - timestamp: new Date().toISOString(), - version: 1, - }; - - const metadataHash = computeMetadataHash(mockMetadata); - - mockEscrowRepository.findOne.mockResolvedValue({ - id: escrowId, - ipfsCid: 'test-cid', - ipfsMetadataHash: metadataHash, - }); - mockProviderService.getJson.mockResolvedValue(mockMetadata); - - const result = await service.verifyMetadata(escrowId); - - expect(result.isValid).toBe(true); - expect(result.computedHash).toBe(metadataHash); - expect(result.storedHash).toBe(metadataHash); - }); - - it('should return invalid when hash mismatch', async () => { - const escrowId = 'test-escrow-id'; - const mockMetadata = { - escrowId, - buyer: 'buyer-1', - amount: '100', - }; - - mockEscrowRepository.findOne.mockResolvedValue({ - id: escrowId, - ipfsCid: 'test-cid', - ipfsMetadataHash: 'wrong-hash', - }); - mockProviderService.getJson.mockResolvedValue(mockMetadata); - - const result = await service.verifyMetadata(escrowId); - - expect(result.isValid).toBe(false); - expect(result.errors).toContain('Hash mismatch detected'); - }); - }); - - describe('getGatewayUrl', () => { - it('should return correct gateway URL', () => { - const cid = 'test-cid'; - const url = service.getGatewayUrl(cid); - - expect(url).toBe('https://gateway.pinata.cloud/ipfs/test-cid'); - }); - }); -}); diff --git a/apps/backend/src/modules/ipfs/ipfs.service.ts b/apps/backend/src/modules/ipfs/ipfs.service.ts deleted file mode 100644 index f653d5c1..00000000 --- a/apps/backend/src/modules/ipfs/ipfs.service.ts +++ /dev/null @@ -1,264 +0,0 @@ -import { - Injectable, - Logger, - Inject, - InternalServerErrorException, - BadRequestException, -} from '@nestjs/common'; -import { ConfigType } from '@nestjs/config'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import ipfsConfig from '../../config/ipfs.config'; -import { IpfsProviderService } from './services/ipfs-provider.service'; -import { - computeMetadataHash, - verifyMetadataHash, -} from './utils/metadata-hash.util'; -import { - EscrowMetadata, - MetadataVerificationResult, -} from './types/ipfs-metadata.types'; -import { Escrow } from '../escrow/entities/escrow.entity'; -import { PartyRole } from '../escrow/entities/party.entity'; -import { ConditionType } from '../escrow/entities/condition.entity'; - -@Injectable() -export class IpfsService { - private readonly logger = new Logger(IpfsService.name); - - constructor( - @Inject(ipfsConfig.KEY) - private config: ConfigType, - private readonly providerService: IpfsProviderService, - @InjectRepository(Escrow) - private escrowRepository: Repository, - ) {} - - /** - * Pins escrow metadata to IPFS and updates the escrow entity - * @param escrowId The escrow ID - * @param metadata The metadata to pin - * @returns Object containing CID and metadata hash - */ - async pinMetadata( - escrowId: string, - metadata: Partial, - ): Promise<{ cid: string; metadataHash: string }> { - try { - this.logger.log(`Pinning metadata for escrow: ${escrowId}`); - - // Fetch existing escrow to get current version and previous CID - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - }); - - if (!escrow) { - throw new BadRequestException(`Escrow ${escrowId} not found`); - } - - // Build complete metadata object - const completeMetadata: EscrowMetadata = { - escrowId: escrow.id, - buyer: - escrow.parties?.find((p) => p.role === PartyRole.BUYER)?.userId ?? '', - seller: - escrow.parties?.find((p) => p.role === PartyRole.SELLER)?.userId ?? - '', - amount: escrow.amount.toString(), - asset: escrow.assetCode, - conditions: - escrow.conditions?.map((c) => ({ - description: c.description, - type: c.type ?? ConditionType.MANUAL, - fulfilled: c.isFulfilled ?? false, - })) ?? [], - deadline: escrow.expiresAt?.toISOString() ?? '', - status: escrow.status, - timestamp: new Date().toISOString(), - version: (escrow.ipfsVersion ?? 0) + 1, - previousCid: escrow.ipfsCid ?? undefined, - ...metadata, - }; - - // Compute SHA-256 hash before pinning - const metadataHash = computeMetadataHash( - completeMetadata as Record, - ); - - // Pin to IPFS - const cid = await this.providerService.pinJson( - completeMetadata as Record, - `escrow-${escrowId}-v${completeMetadata.version}`, - ); - - // Update escrow entity with IPFS information - escrow.ipfsCid = cid; - escrow.ipfsMetadataHash = metadataHash; - escrow.ipfsVersion = completeMetadata.version; - await this.escrowRepository.save(escrow); - - this.logger.log( - `Metadata pinned successfully for escrow ${escrowId}. CID: ${cid}, Version: ${completeMetadata.version}`, - ); - - return { cid, metadataHash }; - } catch (error) { - this.logger.error(`Failed to pin metadata for escrow ${escrowId}`, error); - // Graceful failure - don't block the operation - throw new InternalServerErrorException('IPFS metadata pinning failed'); - } - } - - /** - * Retrieves escrow metadata from IPFS - * @param escrowId The escrow ID - * @returns The metadata object - */ - async getMetadata(escrowId: string): Promise { - try { - this.logger.log(`Retrieving metadata for escrow: ${escrowId}`); - - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - }); - - if (!escrow || !escrow.ipfsCid) { - throw new BadRequestException( - `No IPFS metadata found for escrow ${escrowId}`, - ); - } - - const metadata = await this.providerService.getJson( - escrow.ipfsCid, - ); - - this.logger.log(`Metadata retrieved successfully for escrow ${escrowId}`); - return metadata; - } catch (error) { - this.logger.error( - `Failed to retrieve metadata for escrow ${escrowId}`, - error, - ); - throw new InternalServerErrorException( - 'Failed to retrieve IPFS metadata', - ); - } - } - - /** - * Verifies escrow metadata integrity by comparing hashes - * @param escrowId The escrow ID - * @returns Verification result - */ - async verifyMetadata(escrowId: string): Promise { - try { - this.logger.log(`Verifying metadata for escrow: ${escrowId}`); - - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - }); - - if (!escrow || !escrow.ipfsCid || !escrow.ipfsMetadataHash) { - return { - isValid: false, - escrowId, - computedHash: '', - storedHash: '', - metadata: {} as EscrowMetadata, - errors: ['No IPFS metadata found'], - }; - } - - // Retrieve metadata from IPFS - const metadata = await this.providerService.getJson( - escrow.ipfsCid, - ); - - // Compute hash from retrieved metadata - const computedHash = computeMetadataHash( - metadata as Record, - ); - - const isValid = verifyMetadataHash( - metadata as Record, - escrow.ipfsMetadataHash, - ); - - this.logger.log( - `Metadata verification for escrow ${escrowId}: ${isValid ? 'VALID' : 'INVALID'}`, - ); - - return { - isValid, - escrowId, - computedHash, - storedHash: escrow.ipfsMetadataHash, - metadata, - errors: isValid ? [] : ['Hash mismatch detected'], - }; - } catch (error) { - this.logger.error( - `Failed to verify metadata for escrow ${escrowId}`, - error, - ); - throw new InternalServerErrorException('Metadata verification failed'); - } - } - - /** - * Uploads a file to IPFS (backward compatibility) - * @param fileBuffer The file content as a Buffer - * @param filename The original filename - * @returns The IPFS CID - */ - async uploadFile(fileBuffer: Buffer, filename: string): Promise { - try { - this.logger.log(`Uploading file to IPFS: ${filename}`); - - // For file uploads, we still use Pinata directly since provider service only handles JSON - const formData = new FormData(); - const blob = new Blob([new Uint8Array(fileBuffer)]); - formData.append('file', blob, filename); - - const axios = (await import('axios')).default; - const response = await axios.post<{ - IpfsHash: string; - PinSize: number; - Timestamp: string; - }>('https://api.pinata.cloud/pinning/pinFileToIPFS', formData, { - headers: { - Authorization: `Bearer ${this.config.pinataJwt}`, - }, - }); - - const cid = response.data.IpfsHash; - this.logger.log(`File uploaded successfully to IPFS. CID: ${cid}`); - return cid; - } catch (error) { - this.logger.error(`Failed to upload file to IPFS: ${filename}`, error); - throw new InternalServerErrorException('IPFS upload failed'); - } - } - - /** - * Uploads JSON metadata to IPFS (backward compatibility) - * @param data The JSON data to upload - * @param name The name for the pin - * @returns The IPFS CID - */ - async uploadJson( - data: Record, - name: string, - ): Promise { - return this.providerService.pinJson(data, name); - } - - /** - * Gets the gateway URL for a CID - * @param cid The IPFS CID - * @returns The full gateway URL - */ - getGatewayUrl(cid: string): string { - return `${this.config.gatewayUrl}${cid}`; - } -} diff --git a/apps/backend/src/modules/ipfs/services/ipfs-provider.service.ts b/apps/backend/src/modules/ipfs/services/ipfs-provider.service.ts deleted file mode 100644 index a5062212..00000000 --- a/apps/backend/src/modules/ipfs/services/ipfs-provider.service.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { Injectable, Inject, Logger } from '@nestjs/common'; -import { ConfigType } from '@nestjs/config'; -import axios from 'axios'; -import ipfsConfig from '../../../config/ipfs.config'; - -interface PinataResponse { - IpfsHash: string; - PinSize: number; - Timestamp: string; -} - -interface LocalIpfsResponse { - Name: string; - Hash: string; - Size: string; -} - -export type IpfsProviderType = 'pinata' | 'local'; - -@Injectable() -export class IpfsProviderService { - private readonly logger = new Logger(IpfsProviderService.name); - private readonly provider: IpfsProviderType; - - constructor( - @Inject(ipfsConfig.KEY) - private config: ConfigType, - ) { - this.provider = (config.provider as IpfsProviderType) || 'pinata'; - this.logger.log(`IPFS Provider: ${this.provider}`); - } - - /** - * Uploads JSON metadata to IPFS - * @param data The JSON data to upload - * @param name The name for the pin - * @returns The IPFS CID - */ - async pinJson(data: Record, name: string): Promise { - const retries = this.config.maxRetries; - let lastError: Error | null = null; - - for (let attempt = 0; attempt <= retries; attempt++) { - try { - if (this.provider === 'pinata') { - return await this.pinJsonToPinata(data, name); - } else { - return await this.pinJsonToLocal(data, name); - } - } catch (error) { - lastError = error instanceof Error ? error : new Error(String(error)); - this.logger.warn( - `IPFS pin attempt ${attempt + 1}/${retries + 1} failed: ${lastError.message}`, - ); - if (attempt < retries) { - await this.delay(1000); - } - } - } - - const errorToThrow = lastError ?? new Error('All IPFS pin attempts failed'); - this.logger.error(`All IPFS pin attempts failed`, errorToThrow); - throw errorToThrow; - } - - /** - * Retrieves JSON metadata from IPFS - * @param cid The IPFS CID - * @returns The JSON data - */ - async getJson(cid: string): Promise { - try { - if (this.provider === 'pinata') { - return await this.getJsonFromPinata(cid); - } else { - return await this.getJsonFromLocal(cid); - } - } catch (error) { - this.logger.error(`Failed to retrieve JSON from IPFS: ${cid}`, error); - throw error; - } - } - - private async pinJsonToPinata( - data: Record, - name: string, - ): Promise { - const response = await axios.post( - 'https://api.pinata.cloud/pinning/pinJSONToIPFS', - { - pinataContent: data, - pinataMetadata: { name }, - }, - { - headers: { - Authorization: `Bearer ${this.config.pinataJwt}`, - }, - }, - ); - - return response.data.IpfsHash; - } - - private async pinJsonToLocal( - data: Record, - name: string, - ): Promise { - const formData = new FormData(); - const blob = new Blob([JSON.stringify(data)], { type: 'application/json' }); - formData.append('file', blob, `${name}.json`); - - const response = await axios.post( - `${this.config.localNodeUrl}/api/v0/add`, - formData, - ); - - return response.data.Hash; - } - - private async getJsonFromPinata(cid: string): Promise { - const response = await axios.get(`${this.config.gatewayUrl}${cid}`); - return response.data; - } - - private async getJsonFromLocal(cid: string): Promise { - const response = await axios.get( - `${this.config.localNodeUrl}/api/v0/cat?arg=${cid}`, - ); - return response.data; - } - - private delay(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); - } -} diff --git a/apps/backend/src/modules/ipfs/types/ipfs-metadata.types.ts b/apps/backend/src/modules/ipfs/types/ipfs-metadata.types.ts deleted file mode 100644 index 4c4dfa02..00000000 --- a/apps/backend/src/modules/ipfs/types/ipfs-metadata.types.ts +++ /dev/null @@ -1,29 +0,0 @@ -export interface EscrowMetadata { - escrowId: string; - buyer: string; - seller: string; - amount: string; - asset: string; - conditions: EscrowConditionMetadata[]; - deadline: string; - status: string; - timestamp: string; - version: number; - previousCid?: string; - [key: string]: unknown; // Index signature for Record compatibility -} - -export interface EscrowConditionMetadata { - description: string; - type: string; - fulfilled: boolean; -} - -export interface MetadataVerificationResult { - isValid: boolean; - escrowId: string; - computedHash: string; - storedHash: string; - metadata: EscrowMetadata; - errors?: string[]; -} diff --git a/apps/backend/src/modules/ipfs/utils/metadata-hash.util.spec.ts b/apps/backend/src/modules/ipfs/utils/metadata-hash.util.spec.ts deleted file mode 100644 index ded0fd24..00000000 --- a/apps/backend/src/modules/ipfs/utils/metadata-hash.util.spec.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { computeMetadataHash, verifyMetadataHash } from './metadata-hash.util'; - -describe('Metadata Hash Utility', () => { - describe('computeMetadataHash', () => { - it('should compute consistent hash for same data', () => { - const metadata = { - escrowId: 'test-123', - amount: '100', - status: 'active', - }; - - const hash1 = computeMetadataHash(metadata); - const hash2 = computeMetadataHash(metadata); - - expect(hash1).toBe(hash2); - expect(hash1).toHaveLength(64); // SHA-256 produces 64 hex characters - }); - - it('should produce different hash for different data', () => { - const metadata1 = { - escrowId: 'test-123', - amount: '100', - status: 'active', - }; - - const metadata2 = { - escrowId: 'test-123', - amount: '200', - status: 'active', - }; - - const hash1 = computeMetadataHash(metadata1); - const hash2 = computeMetadataHash(metadata2); - - expect(hash1).not.toBe(hash2); - }); - - it('should be order-independent (canonical JSON)', () => { - const metadata1 = { - escrowId: 'test-123', - amount: '100', - status: 'active', - }; - - const metadata2 = { - status: 'active', - escrowId: 'test-123', - amount: '100', - }; - - const hash1 = computeMetadataHash(metadata1); - const hash2 = computeMetadataHash(metadata2); - - expect(hash1).toBe(hash2); - }); - - it('should handle complex nested objects', () => { - const metadata = { - escrowId: 'test-123', - buyer: 'buyer-1', - seller: 'seller-1', - amount: '100.5', - asset: 'XLM', - conditions: [ - { description: 'Condition 1', type: 'delivery', fulfilled: false }, - { description: 'Condition 2', type: 'approval', fulfilled: true }, - ], - deadline: '2026-12-31T23:59:59.000Z', - status: 'active', - timestamp: new Date().toISOString(), - version: 1, - }; - - const hash = computeMetadataHash(metadata); - - expect(hash).toHaveLength(64); - expect(hash).toMatch(/^[0-9a-f]+$/); // Valid hex string - }); - }); - - describe('verifyMetadataHash', () => { - it('should return true for matching hash', () => { - const metadata = { - escrowId: 'test-123', - amount: '100', - status: 'active', - }; - - const hash = computeMetadataHash(metadata); - const isValid = verifyMetadataHash(metadata, hash); - - expect(isValid).toBe(true); - }); - - it('should return false for mismatched hash', () => { - const metadata = { - escrowId: 'test-123', - amount: '100', - status: 'active', - }; - - const wrongHash = 'a'.repeat(64); - const isValid = verifyMetadataHash(metadata, wrongHash); - - expect(isValid).toBe(false); - }); - - it('should detect tampered metadata', () => { - const originalMetadata = { - escrowId: 'test-123', - amount: '100', - status: 'active', - }; - - const hash = computeMetadataHash(originalMetadata); - - const tamperedMetadata = { - escrowId: 'test-123', - amount: '999', - status: 'active', - }; - - const isValid = verifyMetadataHash(tamperedMetadata, hash); - - expect(isValid).toBe(false); - }); - }); -}); diff --git a/apps/backend/src/modules/ipfs/utils/metadata-hash.util.ts b/apps/backend/src/modules/ipfs/utils/metadata-hash.util.ts deleted file mode 100644 index 9d650853..00000000 --- a/apps/backend/src/modules/ipfs/utils/metadata-hash.util.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { createHash } from 'crypto'; - -/** - * Computes SHA-256 hash of metadata for tamper-proof verification - */ -export function computeMetadataHash(metadata: Record): string { - const canonicalJson = JSON.stringify(metadata, Object.keys(metadata).sort()); - return createHash('sha256').update(canonicalJson).digest('hex'); -} - -/** - * Verifies that stored metadata matches the expected hash - */ -export function verifyMetadataHash( - metadata: Record, - expectedHash: string, -): boolean { - const computedHash = computeMetadataHash(metadata); - return computedHash === expectedHash; -} diff --git a/apps/backend/src/modules/stellar/controllers/stellar-event.controller.ts b/apps/backend/src/modules/stellar/controllers/stellar-event.controller.ts deleted file mode 100644 index b301b19c..00000000 --- a/apps/backend/src/modules/stellar/controllers/stellar-event.controller.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - Controller, - Get, - Post, - Query, - HttpCode, - HttpStatus, -} from '@nestjs/common'; -import { StellarEventListenerService } from '../services/stellar-event-listener.service'; - -@Controller('stellar/events') -export class StellarEventController { - constructor( - private readonly stellarEventListenerService: StellarEventListenerService, - ) {} - - @Get('status') - getSyncStatus() { - return this.stellarEventListenerService.getSyncStatus(); - } - - @Post('sync') - @HttpCode(HttpStatus.OK) - async syncFromLedger(@Query('ledger') ledger?: string) { - const startLedger = ledger ? parseInt(ledger, 10) : undefined; - await this.stellarEventListenerService.syncFromLedger(startLedger || 0); - return { message: `Sync started from ledger: ${startLedger || 0}` }; - } - - @Post('restart') - @HttpCode(HttpStatus.OK) - async restartListener() { - await this.stellarEventListenerService.stopEventListener(); - await this.stellarEventListenerService.startEventListener(); - return { message: 'Event listener restarted' }; - } -} diff --git a/apps/backend/src/modules/stellar/entities/stellar-event.entity.ts b/apps/backend/src/modules/stellar/entities/stellar-event.entity.ts deleted file mode 100644 index b6cb3a7e..00000000 --- a/apps/backend/src/modules/stellar/entities/stellar-event.entity.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - Index, - Unique, -} from 'typeorm'; - -export enum StellarEventType { - ESCROW_CREATED = 'ESCROW_CREATED', - ESCROW_FUNDED = 'ESCROW_FUNDED', - MILESTONE_RELEASED = 'MILESTONE_RELEASED', - ESCROW_COMPLETED = 'ESCROW_COMPLETED', - ESCROW_CANCELLED = 'ESCROW_CANCELLED', - DISPUTE_CREATED = 'DISPUTE_CREATED', - DISPUTE_RESOLVED = 'DISPUTE_RESOLVED', -} - -@Entity('stellar_events') -@Unique(['txHash', 'eventIndex']) -export class StellarEvent { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column({ type: 'varchar', length: 64 }) - @Index() - txHash: string; - - @Column({ type: 'int' }) - eventIndex: number; - - @Column({ - type: 'simple-enum', - enum: StellarEventType, - }) - @Index() - eventType: StellarEventType; - - @Column({ type: 'varchar', nullable: true }) - escrowId?: string; - - @Column({ type: 'int' }) - @Index() - ledger: number; - - @Column({ type: 'datetime' }) - @Index() - timestamp: Date; - - @Column({ type: 'simple-json' }) - rawPayload: Record; // eslint-disable-line @typescript-eslint/no-explicit-any - - @Column({ type: 'simple-json', nullable: true }) - extractedFields?: Record; // eslint-disable-line @typescript-eslint/no-explicit-any - - @CreateDateColumn() - createdAt: Date; - - // Helper fields for extracted structured data - @Column({ type: 'decimal', precision: 18, scale: 7, nullable: true }) - amount?: number; - - @Column({ type: 'varchar', nullable: true, name: 'asset_code' }) - assetCode?: string; - - @Column({ type: 'varchar', nullable: true, name: 'asset_issuer' }) - assetIssuer?: string; - - @Column({ type: 'int', nullable: true }) - milestoneIndex?: number; - - @Column({ type: 'varchar', nullable: true }) - fromAddress?: string; - - @Column({ type: 'varchar', nullable: true }) - toAddress?: string; - - @Column({ type: 'text', nullable: true }) - reason?: string; - - // Monotonic cursor for incremental sync - // Composite of ledger sequence and event index for uniqueness - @Column({ type: 'bigint', name: 'cursor' }) - @Index() - cursor: string; -} diff --git a/apps/backend/src/modules/stellar/services/stellar-cursor-monotonicity.spec.ts b/apps/backend/src/modules/stellar/services/stellar-cursor-monotonicity.spec.ts deleted file mode 100644 index cfae074d..00000000 --- a/apps/backend/src/modules/stellar/services/stellar-cursor-monotonicity.spec.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { ConfigService } from '@nestjs/config'; - -import { - StellarEvent, - StellarEventType, -} from '../entities/stellar-event.entity'; -import { StellarEventListenerService } from './stellar-event-listener.service'; -import { Escrow } from '../../escrow/entities/escrow.entity'; -import { AllowedAsset } from '../../assets/entities/allowed-asset.entity'; - -describe.skip('StellarEvent Cursor Monotonicity Tests', () => { - let service: StellarEventListenerService; - let stellarEventRepository: jest.Mocked>; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - StellarEventListenerService, - { - provide: getRepositoryToken(StellarEvent), - useValue: { - findOne: jest.fn(), - find: jest.fn(), - create: jest.fn(), - save: jest.fn(), - count: jest.fn(), - }, - }, - { - provide: getRepositoryToken(Escrow), - useValue: { - findOne: jest.fn(), - find: jest.fn(), - create: jest.fn(), - save: jest.fn(), - }, - }, - { - provide: getRepositoryToken(AllowedAsset), - useValue: { - find: jest.fn(), - }, - }, - { - provide: ConfigService, - useValue: { - get: jest.fn(), - }, - }, - { - provide: 'SorobanClientService', - useValue: { - getContractId: jest.fn(() => 'test-contract-id'), - getRpc: jest.fn(), - }, - }, - { - provide: 'ConsistencyCheckerService', - useValue: { - checkConsistency: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get( - StellarEventListenerService, - ); - stellarEventRepository = module.get(getRepositoryToken(StellarEvent)); - }); - - describe('StellarEvent Cursor Monotonicity', () => { - it('should compute cursor as composite of ledger and eventIndex', () => { - const ledger = 123456; - const eventIndex = 2; - - // Cursor formula: ledger * 1000 + eventIndex - const expectedCursor = ( - BigInt(ledger) * BigInt(1000) + - BigInt(eventIndex) - ).toString(); - - expect(expectedCursor).toBe('123456002'); - }); - - it('should ensure cursor uniqueness within same ledger', () => { - const ledger = 123456; - - const cursor1 = (BigInt(ledger) * BigInt(1000) + BigInt(0)).toString(); - const cursor2 = (BigInt(ledger) * BigInt(1000) + BigInt(1)).toString(); - const cursor3 = (BigInt(ledger) * BigInt(1000) + BigInt(2)).toString(); - - expect(cursor1).not.toBe(cursor2); - expect(cursor2).not.toBe(cursor3); - expect(cursor3).not.toBe(cursor1); - }); - - it('should ensure cursor monotonicity across ledgers', () => { - const ledger1 = 123456; - const ledger2 = 123457; - - const cursor1 = (BigInt(ledger1) * BigInt(1000) + BigInt(999)).toString(); - const cursor2 = (BigInt(ledger2) * BigInt(1000) + BigInt(0)).toString(); - - expect(BigInt(cursor2)).toBeGreaterThan(BigInt(cursor1)); - }); - - it('should ensure cursor is present in all StellarEvents', () => { - const mockEvent: StellarEvent = { - id: '1', - txHash: 'abc123', - eventIndex: 0, - eventType: StellarEventType.ESCROW_CREATED, - escrowId: 'escrow-1', - ledger: 123456, - timestamp: new Date(), - rawPayload: {}, - extractedFields: {}, - cursor: '123456000', - } as StellarEvent; - - expect(mockEvent.cursor).toBeDefined(); - expect(mockEvent.cursor).toBe('123456000'); - }); - - it('should handle cursor computation for maximum eventIndex', () => { - const ledger = 123456; - const eventIndex = 999; - - const cursor = ( - BigInt(ledger) * BigInt(1000) + - BigInt(eventIndex) - ).toString(); - - expect(cursor).toBe('123456999'); - }); - - it('should verify cursor ordering matches event processing order', () => { - const events = [ - { ledger: 100, eventIndex: 0 }, - { ledger: 100, eventIndex: 1 }, - { ledger: 101, eventIndex: 0 }, - { ledger: 101, eventIndex: 2 }, - ]; - - const cursors = events.map( - (e) => BigInt(e.ledger) * BigInt(1000) + BigInt(e.eventIndex), - ); - - // Verify monotonic increasing - for (let i = 1; i < cursors.length; i++) { - expect(cursors[i]).toBeGreaterThan(cursors[i - 1]); - } - }); - }); - - describe('Cursor-Based Incremental Sync', () => { - it('should support resuming from last cursor', () => { - const lastCursor = '123456500'; - const nextLedger = 123457; - - // Simulate resuming from cursor - const lastLedgerFromCursor = BigInt(lastCursor) / BigInt(1000); - expect(lastLedgerFromCursor).toBe(BigInt(123456)); - - // Next events should have cursor > lastCursor - const nextCursor = ( - BigInt(nextLedger) * BigInt(1000) + - BigInt(0) - ).toString(); - expect(BigInt(nextCursor)).toBeGreaterThan(BigInt(lastCursor)); - }); - - it('should allow filtering events by cursor range', () => { - const events = [ - { cursor: '100000' }, - { cursor: '200000' }, - { cursor: '300000' }, - { cursor: '400000' }, - ]; - - const afterCursor = '200000'; - const beforeCursor = '400000'; - - const filtered = events.filter( - (e) => - BigInt(e.cursor) > BigInt(afterCursor) && - BigInt(e.cursor) < BigInt(beforeCursor), - ); - - expect(filtered.length).toBe(1); - expect(filtered[0].cursor).toBe('300000'); - }); - }); -}); diff --git a/apps/backend/src/modules/stellar/services/stellar-event-listener.service.spec.ts b/apps/backend/src/modules/stellar/services/stellar-event-listener.service.spec.ts deleted file mode 100644 index c0290c6d..00000000 --- a/apps/backend/src/modules/stellar/services/stellar-event-listener.service.spec.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { StellarEventListenerService } from './stellar-event-listener.service'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { - StellarEvent, - StellarEventType, -} from '../entities/stellar-event.entity'; -import { Escrow, EscrowStatus } from '../../escrow/entities/escrow.entity'; -import { SorobanClientService } from '../../../services/stellar/soroban-client.service'; -import { ConfigService } from '@nestjs/config'; -import { ConsistencyCheckerService } from '../../admin/services/consistency-checker.service'; - -describe('StellarEventListenerService', () => { - let service: StellarEventListenerService; - let stellarEventRepo: jest.Mocked; - let escrowRepo: jest.Mocked; - let sorobanClient: jest.Mocked; - let rpcServer: jest.Mocked; - - beforeEach(async () => { - rpcServer = { - getLatestLedger: jest.fn().mockResolvedValue({ sequence: 100 }), - getEvents: jest.fn().mockResolvedValue({ events: [] }), - }; - - const module: TestingModule = await Test.createTestingModule({ - providers: [ - StellarEventListenerService, - { - provide: ConfigService, - useValue: { - get: jest.fn().mockReturnValue(0), - }, - }, - { - provide: getRepositoryToken(StellarEvent), - useValue: { - findOne: jest.fn(), - save: jest.fn(), - create: jest.fn().mockImplementation((dto) => dto), - }, - }, - { - provide: getRepositoryToken(Escrow), - useValue: { - findOne: jest.fn(), - save: jest.fn(), - create: jest.fn().mockImplementation((dto) => dto), - }, - }, - { - provide: SorobanClientService, - useValue: { - getContractId: jest.fn().mockReturnValue('contract-id'), - getRpc: jest.fn().mockReturnValue(rpcServer), - }, - }, - { - provide: ConsistencyCheckerService, - useValue: { - checkConsistency: jest.fn().mockResolvedValue({}), - }, - }, - ], - }).compile(); - - service = module.get( - StellarEventListenerService, - ); - stellarEventRepo = module.get(getRepositoryToken(StellarEvent)); - escrowRepo = module.get(getRepositoryToken(Escrow)); - sorobanClient = module.get(SorobanClientService); - - // Mock sleep and pollEvents to avoid waiting and infinite loop - (service as any).sleep = jest.fn().mockResolvedValue(undefined); - jest.spyOn(service as any, 'pollEvents').mockResolvedValue(undefined); - - // Initialize server and contractId - (service as any).server = rpcServer; - (service as any).contractId = 'contract-id'; - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); - - describe('onModuleInit', () => { - it('should initialize and start listener', async () => { - const startSpy = jest - .spyOn(service, 'startEventListener') - .mockResolvedValue(); - await service.onModuleInit(); - expect(sorobanClient.getContractId).toHaveBeenCalled(); - expect(startSpy).toHaveBeenCalled(); - }); - }); - - describe('startEventListener', () => { - it('should set isRunning to true and poll events', async () => { - // Mock pollEvents to avoid infinite loop - const pollSpy = jest - .spyOn(service as any, 'pollEvents') - .mockResolvedValue(undefined); - await service.startEventListener(); - expect(service.getSyncStatus().isRunning).toBe(true); - expect(pollSpy).toHaveBeenCalled(); - }); - }); - - describe('processNewEvents', () => { - it('should not process if no new ledgers', async () => { - (service as any).lastProcessedLedger = 100; - rpcServer.getLatestLedger.mockResolvedValue({ sequence: 100 }); - await (service as any).processNewEvents(); - expect(rpcServer.getEvents).not.toHaveBeenCalled(); - }); - - it('should process new ledgers if available', async () => { - (service as any).lastProcessedLedger = 90; - rpcServer.getLatestLedger.mockResolvedValue({ sequence: 100 }); - rpcServer.getEvents.mockResolvedValue({ events: [] }); - await (service as any).processNewEvents(); - expect(rpcServer.getEvents).toHaveBeenCalled(); - expect((service as any).lastProcessedLedger).toBe(100); - }); - }); - - describe('handleEscrowFunded', () => { - it('should update status to ACTIVE', async () => { - const mockEscrow = { id: 'e1', status: EscrowStatus.PENDING }; - escrowRepo.findOne.mockResolvedValue(mockEscrow); - - const event = { - escrowId: 'e1', - eventType: StellarEventType.ESCROW_FUNDED, - } as any; - await (service as any).handleEscrowFunded(event); - - expect(mockEscrow.status).toBe(EscrowStatus.ACTIVE); - expect(escrowRepo.save).toHaveBeenCalledWith(mockEscrow); - }); - }); - - describe('stopEventListener', () => { - it('should set isRunning to false', async () => { - await service.startEventListener(); - await service.stopEventListener(); - expect(service.getSyncStatus().isRunning).toBe(false); - }); - }); -}); diff --git a/apps/backend/src/modules/stellar/services/stellar-event-listener.service.ts b/apps/backend/src/modules/stellar/services/stellar-event-listener.service.ts deleted file mode 100644 index 8205666d..00000000 --- a/apps/backend/src/modules/stellar/services/stellar-event-listener.service.ts +++ /dev/null @@ -1,729 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/require-await */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { - Injectable, - Logger, - OnModuleInit, - OnModuleDestroy, - Inject, - forwardRef, -} from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { rpc, xdr, Address, Asset } from '@stellar/stellar-sdk'; -import { - StellarEvent, - StellarEventType, -} from '../entities/stellar-event.entity'; -import { Escrow, EscrowStatus } from '../../escrow/entities/escrow.entity'; -import { SorobanClientService } from '../../../services/stellar/soroban-client.service'; -import { ConsistencyCheckerService } from '../../admin/services/consistency-checker.service'; -import { AllowedAsset } from '../../assets/entities/allowed-asset.entity'; - -@Injectable() -export class StellarEventListenerService - implements OnModuleInit, OnModuleDestroy -{ - private readonly logger = new Logger(StellarEventListenerService.name); - private server: rpc.Server; - private contractId: string; - private isRunning = false; - private lastProcessedLedger = 0; - private reconnectAttempts = 0; - private maxReconnectAttempts = 5; - private reconnectDelay = 5000; // 5 seconds - private abortController: AbortController | null = null; - - constructor( - private configService: ConfigService, - @InjectRepository(StellarEvent) - private stellarEventRepository: Repository, - @InjectRepository(Escrow) - private escrowRepository: Repository, - private sorobanClient: SorobanClientService, - @Inject(forwardRef(() => ConsistencyCheckerService)) - private consistencyChecker: ConsistencyCheckerService, - ) {} - - async onModuleInit() { - this.contractId = this.sorobanClient.getContractId(); - this.server = this.sorobanClient.getRpc(); - - if (!this.contractId) { - this.logger.error('Missing required configuration: STELLAR_CONTRACT_ID'); - return; - } - - void this.startEventListener(); - } - - async onModuleDestroy() { - await this.stopEventListener(); - } - - async startEventListener() { - if (this.isRunning) { - this.logger.warn('Event listener is already running'); - return; - } - - this.abortController = new AbortController(); - this.isRunning = true; - this.logger.log( - `Starting Stellar event listener for contract: ${this.contractId}`, - ); - - try { - // Get the last processed ledger from database - await this.initializeLastProcessedLedger(); - - // Start the event polling loop - await this.pollEvents(); - } catch (error) { - if ((error as Error).name !== 'AbortError') { - this.logger.error('Failed to start event listener:', error); - this.isRunning = false; - } - } - } - - async stopEventListener() { - this.isRunning = false; - if (this.abortController) { - this.abortController.abort(); - this.abortController = null; - } - this.logger.log('Stopped Stellar event listener'); - } - - private async initializeLastProcessedLedger() { - const lastEvent = await this.stellarEventRepository.findOne({ - where: {}, - order: { ledger: 'DESC' }, - }); - - if (lastEvent) { - this.lastProcessedLedger = lastEvent.ledger; - this.logger.log(`Resuming from ledger: ${this.lastProcessedLedger}`); - } else { - // Start from a configurable ledger or current - const startLedger = this.configService.get( - 'STELLAR_START_LEDGER', - 0, - ); - this.lastProcessedLedger = startLedger; - this.logger.log(`Starting from ledger: ${this.lastProcessedLedger}`); - } - } - - private async pollEvents() { - let delay = 10000; - while (this.isRunning) { - try { - await this.processNewEvents(); - delay = 10000; - this.reconnectAttempts = 0; - await this.sleep(delay, this.abortController?.signal); - } catch (error) { - if ((error as Error).name === 'AbortError') break; - this.reconnectAttempts++; - - if (this.reconnectAttempts > this.maxReconnectAttempts) { - this.logger.error( - `Max reconnection attempts (${this.maxReconnectAttempts}) reached. Stopping event listener.`, - ); - this.isRunning = false; - break; - } - - const backoffDelay = - this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1); - this.logger.error( - `Error during event polling: ${(error as Error).message}. Reconnecting in ${backoffDelay / 1000}s (Attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`, - ); - - try { - await this.sleep(backoffDelay, this.abortController?.signal); - } catch (sleepErr) { - if ((sleepErr as Error).name === 'AbortError') break; - } - } - } - } - - private async processNewEvents() { - const latestLedgerResponse = await this.server.getLatestLedger(); - const latestLedger = latestLedgerResponse.sequence; - - if (latestLedger <= this.lastProcessedLedger) { - return; // No new ledgers to process - } - - this.logger.debug( - `Processing ledgers ${this.lastProcessedLedger + 1} to ${latestLedger}`, - ); - - await this.processLedgerRange(this.lastProcessedLedger + 1, latestLedger); - this.lastProcessedLedger = latestLedger; - } - - private async processLedgerRange(startLedger: number, endLedger: number) { - try { - const events = await this.getEventsForLedgerRange(startLedger, endLedger); - - for (const event of events) { - await this.processEvent(event); - } - } catch (error) { - this.logger.error( - `Error processing ledger range ${startLedger}-${endLedger}:`, - error, - ); - } - } - - private async getEventsForLedgerRange( - startLedger: number, - endLedger: number, - ) { - const allEvents: any[] = []; - let currentStart = startLedger; - - try { - while (currentStart <= endLedger) { - // Soroban getEvents might have a limit on range - const response = await this.server.getEvents({ - startLedger: currentStart, - filters: [ - { - type: 'contract', - contractIds: [this.contractId], - }, - ], - limit: 100, - }); - - if (!response.events || response.events.length === 0) { - break; - } - - let index = 0; - let lastTxHash = ''; - for (const event of response.events) { - if (event.txHash === lastTxHash) { - index++; - } else { - index = 0; - lastTxHash = event.txHash; - } - - allEvents.push({ - txHash: event.txHash, - eventIndex: index, - event: event, - ledger: event.ledger, - timestamp: new Date(event.ledgerClosedAt), - }); - } - - // Update currentStart based on last event ledger or just break if we reached endLedger - const lastLedger = response.events[response.events.length - 1].ledger; - if (lastLedger >= endLedger) break; - currentStart = lastLedger + 1; - - if (response.events.length < 100) break; - } - } catch (error) { - this.logger.error( - `Failed to get Soroban events for range ${startLedger}-${endLedger}:`, - error, - ); - } - - return allEvents; - } - - private isContractEvent(event: any): boolean { - return event.contractId === this.contractId; - } - - private async processEvent(eventData: any) { - try { - const { txHash, eventIndex, event, ledger, timestamp } = eventData; - - // Check for idempotency - const existingEvent = await this.stellarEventRepository.findOne({ - where: { txHash, eventIndex }, - }); - - if (existingEvent) { - this.logger.debug(`Event already processed: ${txHash}:${eventIndex}`); - return; - } - - // Parse and normalize the event - const normalizedEvent = await this.normalizeEvent( - event, - txHash, - eventIndex, - ledger, - timestamp, - ); - - // Save the normalized event - await this.stellarEventRepository.save(normalizedEvent); - - // Update related escrow records - await this.updateEscrowFromEvent(normalizedEvent); - - this.logger.debug( - `Processed event: ${normalizedEvent.eventType} for escrow: ${normalizedEvent.escrowId}`, - ); - } catch (error) { - this.logger.error( - `Error processing event ${eventData.txHash}:${eventData.eventIndex}:`, - error, - ); - } - } - - private async normalizeEvent( - event: any, - txHash: string, - eventIndex: number, - ledger: number, - timestamp: Date, - ): Promise { - const eventType = this.mapEventType(event); - const extractedFields = await this.extractEventFields(event, eventType); - - // Compute monotonic cursor: composite of ledger and eventIndex - // Format: ledger * 1000 + eventIndex to ensure uniqueness within ledger - const cursor = ( - BigInt(ledger) * BigInt(1000) + - BigInt(eventIndex) - ).toString(); - - return this.stellarEventRepository.create({ - txHash, - eventIndex, - eventType, - escrowId: extractedFields.escrowId, - ledger, - timestamp, - rawPayload: event, - extractedFields, - amount: extractedFields.amount, - assetCode: extractedFields.assetCode, - assetIssuer: extractedFields.assetIssuer, - milestoneIndex: extractedFields.milestoneIndex, - fromAddress: extractedFields.fromAddress, - toAddress: extractedFields.toAddress, - reason: extractedFields.reason, - cursor, - }); - } - - private async extractEventFields( - event: any, - eventType: StellarEventType, - ): Promise> { - const fields: Record = {}; - - try { - // Soroban event structure in getEvents response: - // event.topic: Array of ScVal (XDR base64) - // event.value: ScVal (XDR base64) - - const topics = event.topic.map((t: string) => - xdr.ScVal.fromXDR(t, 'base64'), - ); - const value = xdr.ScVal.fromXDR(event.value, 'base64'); - - // First topic is always the event name (Symbol) - // Second topic is usually the escrow ID (U64) - if (topics.length > 1) { - fields.escrowId = topics[1].u64().low.toString(); - } - - switch (eventType) { - case StellarEventType.ESCROW_CREATED: { - const createdVec = value.vec(); - if (createdVec) { - fields.fromAddress = Address.fromScVal(createdVec[0]).toString(); - fields.toAddress = Address.fromScVal(createdVec[1]).toString(); - - const milestonesVec = createdVec[3].vec(); - if (milestonesVec) { - let totalAmount = 0; - milestonesVec.forEach((m: any) => { - const map = m.map(); - if (map) { - map.forEach((entry: any) => { - const keySym = entry.key().sym().toString(); - if (keySym === 'amount') { - totalAmount += Number(entry.val().i128().lo().toString()); - } - }); - } - }); - - const tokenContractId = Address.fromScVal( - createdVec[2], - ).toString(); - let decimals = 7; - const asset = await this.getAssetByContractId(tokenContractId); - if (asset) { - fields.assetCode = asset.code; - fields.assetIssuer = asset.issuer; - decimals = asset.decimals; - } else { - fields.assetCode = - tokenContractId === - 'CDLZFC3SYJYDZT7K67VZ75YJFCGSN5W4B77T2YI2EHCWH6I6D6LNCU6B' - ? 'XLM' - : 'UNKNOWN'; - } - fields.amount = totalAmount / Math.pow(10, decimals); - } - } - break; - } - - case StellarEventType.ESCROW_FUNDED: - // Value: funder (Address) - fields.fromAddress = Address.fromScVal(value).toString(); - break; - - case StellarEventType.MILESTONE_RELEASED: { - // Topics: [Symbol("milestone_released"), escrow_id, milestone_index] - // Value: amount (i128) - fields.milestoneIndex = topics[2].u32(); - const amountParts = value.i128(); - fields.amount = amountParts.lo().toString(); - break; - } - - case StellarEventType.ESCROW_COMPLETED: - case StellarEventType.ESCROW_CANCELLED: - // Value: () - break; - - case StellarEventType.DISPUTE_CREATED: - // Topics: [Symbol("dispute_raised"), escrow_id, caller] - fields.fromAddress = Address.fromScVal(topics[2]).toString(); - break; - - case StellarEventType.DISPUTE_RESOLVED: - // Topics: [Symbol("dispute_resolved"), escrow_id, winner] - fields.toAddress = Address.fromScVal(topics[2]).toString(); - // Value: split_winner_amount (Option) - if (value.switch() === xdr.ScValType.scvVec()) { - const vec = value.vec(); - if (vec && vec.length > 0) { - fields.amount = Number(vec[0].i128().lo().toString()); - } - } - break; - } - } catch (error) { - this.logger.error(`Error extracting fields from Soroban event:`, error); - } - - return fields; - } - - private async getAssetByContractId( - contractAddress: string, - ): Promise { - const assets = await this.stellarEventRepository.manager - .getRepository(AllowedAsset) - .find({ - where: { active: true }, - }); - - const networkPassphrase = - this.configService.get('stellar.networkPassphrase') || - 'Test SDF Network ; September 2015'; - - for (const asset of assets) { - let assetContractId: string; - if (asset.code === 'XLM') { - assetContractId = - 'CDLZFC3SYJYDZT7K67VZ75YJFCGSN5W4B77T2YI2EHCWH6I6D6LNCU6B'; - } else { - const stellarAsset = new Asset(asset.code, asset.issuer); - assetContractId = stellarAsset.contractId(networkPassphrase); - } - - if (assetContractId === contractAddress) { - return asset; - } - } - return null; - } - - private mapEventType(event: any): StellarEventType { - try { - const topic0 = xdr.ScVal.fromXDR(event.topic[0], 'base64'); - const eventName = topic0.sym().toString(); - - switch (eventName) { - case 'escrow_created': - return StellarEventType.ESCROW_CREATED; - case 'escrow_funded': - return StellarEventType.ESCROW_FUNDED; - case 'milestone_released': - return StellarEventType.MILESTONE_RELEASED; - case 'escrow_completed': - return StellarEventType.ESCROW_COMPLETED; - case 'escrow_cancelled': - return StellarEventType.ESCROW_CANCELLED; - case 'dispute_raised': // Note: contract uses "dispute_raised" - return StellarEventType.DISPUTE_CREATED; - case 'dispute_resolved': - return StellarEventType.DISPUTE_RESOLVED; - default: - this.logger.warn(`Unknown Soroban event topic: ${eventName}`); - return eventName as any; - } - } catch (error) { - this.logger.error('Error mapping event type:', error); - return 'unknown' as any; - } - } - - private async updateEscrowFromEvent(event: StellarEvent) { - if (!event.escrowId) { - return; // No escrow ID to update - } - - try { - switch (event.eventType) { - case StellarEventType.ESCROW_CREATED: - await this.handleEscrowCreated(event); - break; - - case StellarEventType.ESCROW_FUNDED: - await this.handleEscrowFunded(event); - break; - - case StellarEventType.MILESTONE_RELEASED: - this.handleMilestoneReleased(event); - break; - - case StellarEventType.ESCROW_COMPLETED: - await this.handleEscrowCompleted(event); - break; - - case StellarEventType.ESCROW_CANCELLED: - await this.handleEscrowCancelled(event); - break; - - case StellarEventType.DISPUTE_CREATED: - await this.handleDisputeCreated(event); - break; - - case StellarEventType.DISPUTE_RESOLVED: - await this.handleDisputeResolved(event); - break; - } - } catch (error) { - this.logger.error( - `Error updating escrow from event ${event.eventType}:`, - error, - ); - } - } - - private async checkStateMismatch( - escrowId: string, - expectedStatus: EscrowStatus, - ) { - const escrow = await this.escrowRepository.findOne({ - where: { id: escrowId }, - }); - if (escrow && escrow.status !== expectedStatus) { - this.logger.warn( - `State mismatch detected for escrow ${escrowId}: DB status is '${escrow.status}', but on-chain event indicates status should be '${expectedStatus}'.`, - ); - try { - await this.consistencyChecker.checkConsistency({ - escrowIds: [Number(escrowId)], - }); - } catch (err) { - this.logger.error( - `Failed to run consistency check for escrow ${escrowId}:`, - err, - ); - } - } - } - - private async handleEscrowCreated(event: StellarEvent) { - if (!event.escrowId) return; - // Check if escrow already exists - const escrow = await this.escrowRepository.findOne({ - where: { id: event.escrowId }, - }); - - if (!escrow) { - // Create new escrow from event data - const newEscrow = this.escrowRepository.create({ - id: event.escrowId, - title: `Escrow ${event.escrowId}`, // Extract from event if available - amount: event.amount || 0, - assetCode: event.assetCode || 'XLM', - assetIssuer: event.assetIssuer || null, - status: EscrowStatus.PENDING, - creatorId: event.fromAddress, // This would need to be mapped to user ID - isActive: true, - createdAt: event.timestamp, - updatedAt: event.timestamp, - } as any); - - await this.escrowRepository.save(newEscrow); - this.logger.log(`Created new escrow from blockchain: ${event.escrowId}`); - } else { - await this.checkStateMismatch(event.escrowId, EscrowStatus.PENDING); - } - } - - private async handleEscrowFunded(event: StellarEvent) { - if (!event.escrowId) return; - const escrow = await this.escrowRepository.findOne({ - where: { id: event.escrowId }, - }); - - if (escrow) { - await this.checkStateMismatch(event.escrowId, EscrowStatus.ACTIVE); - if (escrow.status === EscrowStatus.PENDING) { - escrow.status = EscrowStatus.ACTIVE; - await this.escrowRepository.save(escrow); - this.logger.log(`Updated escrow status to ACTIVE: ${event.escrowId}`); - } - } - } - - private handleMilestoneReleased(event: StellarEvent): void { - // This would update milestone-specific data - // For now, just log the event - this.logger.log( - `Milestone released for escrow: ${event.escrowId}, milestone: ${event.milestoneIndex}`, - ); - } - - private async handleEscrowCompleted(event: StellarEvent) { - if (!event.escrowId) return; - const escrow = await this.escrowRepository.findOne({ - where: { id: event.escrowId }, - }); - - if (escrow) { - await this.checkStateMismatch(event.escrowId, EscrowStatus.COMPLETED); - if (!this.isTerminalStatus(escrow.status)) { - escrow.status = EscrowStatus.COMPLETED; - escrow.isActive = false; - await this.escrowRepository.save(escrow); - this.logger.log(`Completed escrow: ${event.escrowId}`); - } - } - } - - private async handleEscrowCancelled(event: StellarEvent) { - if (!event.escrowId) return; - const escrow = await this.escrowRepository.findOne({ - where: { id: event.escrowId }, - }); - - if (escrow) { - await this.checkStateMismatch(event.escrowId, EscrowStatus.CANCELLED); - if (!this.isTerminalStatus(escrow.status)) { - escrow.status = EscrowStatus.CANCELLED; - escrow.isActive = false; - await this.escrowRepository.save(escrow); - this.logger.log(`Cancelled escrow: ${event.escrowId}`); - } - } - } - - private async handleDisputeCreated(event: StellarEvent) { - if (!event.escrowId) return; - const escrow = await this.escrowRepository.findOne({ - where: { id: event.escrowId }, - }); - - if (escrow) { - await this.checkStateMismatch(event.escrowId, EscrowStatus.DISPUTED); - if (escrow.status === EscrowStatus.ACTIVE) { - escrow.status = EscrowStatus.DISPUTED; - await this.escrowRepository.save(escrow); - this.logger.log(`Escrow disputed: ${event.escrowId}`); - } - } - } - - private async handleDisputeResolved(event: StellarEvent) { - if (!event.escrowId) return; - this.logger.log(`Dispute resolved for escrow: ${event.escrowId}`); - try { - await this.consistencyChecker.checkConsistency({ - escrowIds: [Number(event.escrowId)], - }); - } catch (err) { - this.logger.error( - `Failed to trigger consistency check after dispute resolved:`, - err, - ); - } - } - - private isTerminalStatus(status: EscrowStatus): boolean { - return [EscrowStatus.COMPLETED, EscrowStatus.CANCELLED].includes(status); - } - - private sleep(ms: number, signal?: AbortSignal): Promise { - return new Promise((resolve, reject) => { - if (signal?.aborted) { - const err = new Error('AbortError'); - err.name = 'AbortError'; - reject(err); - return; - } - - const timeout = setTimeout(resolve, ms); - - signal?.addEventListener('abort', () => { - clearTimeout(timeout); - const err = new Error('AbortError'); - err.name = 'AbortError'; - reject(err); - }); - }); - } - - // Public methods for external control - async syncFromLedger(ledger: number): Promise { - this.lastProcessedLedger = ledger - 1; - this.logger.log(`Manual sync requested from ledger: ${ledger}`); - await this.processNewEvents(); - } - - getSyncStatus(): { - isRunning: boolean; - lastProcessedLedger: number; - reconnectAttempts: number; - } { - return { - isRunning: this.isRunning, - lastProcessedLedger: this.lastProcessedLedger, - reconnectAttempts: this.reconnectAttempts, - }; - } -} diff --git a/apps/backend/src/modules/stellar/stellar-event.module.ts b/apps/backend/src/modules/stellar/stellar-event.module.ts deleted file mode 100644 index 89ac4724..00000000 --- a/apps/backend/src/modules/stellar/stellar-event.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Module, forwardRef } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { ConfigModule } from '@nestjs/config'; -import { StellarEvent } from './entities/stellar-event.entity'; -import { Escrow } from '../escrow/entities/escrow.entity'; -import { StellarEventListenerService } from './services/stellar-event-listener.service'; -import { StellarEventController } from './controllers/stellar-event.controller'; -import { AdminModule } from '../admin/admin.module'; - -@Module({ - imports: [ - ConfigModule, - TypeOrmModule.forFeature([StellarEvent, Escrow]), - forwardRef(() => AdminModule), - ], - controllers: [StellarEventController], - providers: [StellarEventListenerService], - exports: [StellarEventListenerService], -}) -export class StellarEventModule {} diff --git a/apps/backend/src/modules/stellar/stellar.module.ts b/apps/backend/src/modules/stellar/stellar.module.ts deleted file mode 100644 index f6820c28..00000000 --- a/apps/backend/src/modules/stellar/stellar.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module, Global } from '@nestjs/common'; -import { ConfigModule } from '@nestjs/config'; -import stellarConfig from '../../config/stellar.config'; -import { StellarService } from '../../services/stellar.service'; -import { EscrowOperationsService } from '../../services/stellar/escrow-operations'; -import { SorobanClientService } from '../../services/stellar/soroban-client.service'; - -@Global() -@Module({ - imports: [ConfigModule.forFeature(stellarConfig)], - providers: [StellarService, EscrowOperationsService, SorobanClientService], - exports: [ - StellarService, - EscrowOperationsService, - SorobanClientService, - ConfigModule.forFeature(stellarConfig), - ], -}) -export class StellarModule {} diff --git a/apps/backend/src/modules/user/entities/email-verification.entity.ts b/apps/backend/src/modules/user/entities/email-verification.entity.ts deleted file mode 100644 index f50b7e9b..00000000 --- a/apps/backend/src/modules/user/entities/email-verification.entity.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - ManyToOne, - JoinColumn, -} from 'typeorm'; -import { User } from './user.entity'; - -@Entity('email_verifications') -export class EmailVerification { - @PrimaryGeneratedColumn('uuid') - id!: string; - - @Column() - userId!: string; - - @ManyToOne(() => User) - @JoinColumn({ name: 'userId' }) - user!: User; - - @Column({ unique: true }) - token!: string; - - @Column() - expiresAt!: Date; - - @Column({ default: false }) - isUsed!: boolean; - - @CreateDateColumn() - createdAt!: Date; -} diff --git a/apps/backend/src/modules/user/entities/refresh-token.entity.ts b/apps/backend/src/modules/user/entities/refresh-token.entity.ts deleted file mode 100644 index 721911d5..00000000 --- a/apps/backend/src/modules/user/entities/refresh-token.entity.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - ManyToOne, - CreateDateColumn, -} from 'typeorm'; -import { User } from './user.entity'; - -@Entity('refresh_tokens') -export class RefreshToken { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - token: string; - - @Column() - userId: string; - - @ManyToOne(() => User) - user: User; - - @Column() - expiresAt: Date; - - @Column({ default: true }) - isActive: boolean; - - @CreateDateColumn() - createdAt: Date; -} diff --git a/apps/backend/src/modules/user/entities/user-role.enum.ts b/apps/backend/src/modules/user/entities/user-role.enum.ts deleted file mode 100644 index 5e76f2ee..00000000 --- a/apps/backend/src/modules/user/entities/user-role.enum.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum UserRole { - USER = 'USER', - ADMIN = 'ADMIN', - SUPER_ADMIN = 'SUPER_ADMIN', -} diff --git a/apps/backend/src/modules/user/entities/user.entity.ts b/apps/backend/src/modules/user/entities/user.entity.ts deleted file mode 100644 index c18ec9bf..00000000 --- a/apps/backend/src/modules/user/entities/user.entity.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, -} from 'typeorm'; -import { UserRole } from './user-role.enum'; -export { UserRole } from './user-role.enum'; - -@Entity('users') -export class User { - @PrimaryGeneratedColumn('uuid') - id!: string; - - @Column({ unique: true }) - walletAddress!: string; - - @Column({ nullable: true }) - nonce?: string; - - @Column({ default: true }) - isActive!: boolean; - - @Column({ - type: 'text', - enum: UserRole, - default: UserRole.USER, - }) - role!: UserRole; - - // New profile fields - @Column({ type: 'varchar', length: 100, nullable: true }) - displayName?: string; - - @Column({ type: 'varchar', length: 255, nullable: true, unique: true }) - email?: string; - - @Column({ default: false }) - emailVerified!: boolean; - - @Column({ type: 'varchar', length: 500, nullable: true }) - avatarUrl?: string; - - @Column({ type: 'text', nullable: true }) - bio?: string; - - @Column({ type: 'varchar', length: 20, default: 'XLM' }) - preferredAsset!: string; - - // @ManyToOne(() => Organization, (org: Organization) => org.users, { nullable: false }) - // @JoinColumn({ name: 'org_id' }) - // organization!: Organization; - - @CreateDateColumn() - createdAt!: Date; - - @UpdateDateColumn() - updatedAt!: Date; -} diff --git a/apps/backend/src/modules/user/user.module.ts b/apps/backend/src/modules/user/user.module.ts deleted file mode 100644 index a86179b9..00000000 --- a/apps/backend/src/modules/user/user.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { RefreshToken } from './entities/refresh-token.entity'; -import { User } from './entities/user.entity'; -import { EmailVerification } from './entities/email-verification.entity'; -import { UserService } from './user.service'; - -@Module({ - imports: [TypeOrmModule.forFeature([User, RefreshToken, EmailVerification])], - providers: [UserService], - exports: [UserService, TypeOrmModule], -}) -export class UserModule {} diff --git a/apps/backend/src/modules/user/user.service.spec.ts b/apps/backend/src/modules/user/user.service.spec.ts deleted file mode 100644 index 073a62ec..00000000 --- a/apps/backend/src/modules/user/user.service.spec.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { UserService } from './user.service'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { User } from './entities/user.entity'; -import { RefreshToken } from './entities/refresh-token.entity'; -import { Repository } from 'typeorm'; - -describe('UserService', () => { - let service: UserService; - let userRepo: jest.Mocked>; - let refreshTokenRepo: jest.Mocked>; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - UserService, - { - provide: getRepositoryToken(User), - useValue: { - findOne: jest.fn(), - create: jest.fn(), - save: jest.fn(), - update: jest.fn(), - }, - }, - { - provide: getRepositoryToken(RefreshToken), - useValue: { - findOne: jest.fn(), - create: jest.fn(), - save: jest.fn(), - update: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(UserService); - userRepo = module.get(getRepositoryToken(User)); - refreshTokenRepo = module.get(getRepositoryToken(RefreshToken)); - }); - - const mockUser = { id: 'u1', walletAddress: 'GD...123', isActive: true }; - - describe('findByWalletAddress', () => { - it('should call findOne with correct params', async () => { - userRepo.findOne.mockResolvedValue(mockUser as any); - const result = await service.findByWalletAddress('GD...123'); - expect(userRepo.findOne).toHaveBeenCalledWith({ - where: { walletAddress: 'GD...123' }, - }); - expect(result).toEqual(mockUser); - }); - }); - - describe('findById', () => { - it('should call findOne with correct params', async () => { - userRepo.findOne.mockResolvedValue(mockUser as any); - const result = await service.findById('u1'); - expect(userRepo.findOne).toHaveBeenCalledWith({ - where: { id: 'u1', isActive: true }, - }); - expect(result).toEqual(mockUser); - }); - }); - - describe('create', () => { - it('should create and save a user', async () => { - userRepo.create.mockReturnValue(mockUser as any); - userRepo.save.mockResolvedValue(mockUser as any); - const result = await service.create({ walletAddress: 'GD...123' }); - expect(userRepo.create).toHaveBeenCalled(); - expect(userRepo.save).toHaveBeenCalled(); - expect(result).toBe(mockUser); - }); - }); - - describe('update', () => { - it('should update and return updated user', async () => { - userRepo.update.mockResolvedValue({} as any); - userRepo.findOne.mockResolvedValue(mockUser as any); - const result = await service.update('u1', { isActive: false }); - expect(userRepo.update).toHaveBeenCalledWith('u1', { isActive: false }); - expect(result).toBe(mockUser); - }); - - it('should throw if user not found after update', async () => { - userRepo.update.mockResolvedValue({} as any); - userRepo.findOne.mockResolvedValue(null); - await expect(service.update('u1', {})).rejects.toThrow('User not found'); - }); - }); - - describe('refreshToken operations', () => { - const mockToken = { token: 't1', user: mockUser }; - - it('should create and save a refresh token', async () => { - refreshTokenRepo.create.mockReturnValue(mockToken as any); - refreshTokenRepo.save.mockResolvedValue(mockToken as any); - const result = await service.createRefreshToken(mockToken as any); - expect(result).toBe(mockToken); - }); - - it('should find refresh token', async () => { - refreshTokenRepo.findOne.mockResolvedValue(mockToken as any); - const result = await service.findRefreshToken('t1'); - expect(refreshTokenRepo.findOne).toHaveBeenCalledWith({ - where: { token: 't1', isActive: true }, - relations: ['user'], - }); - expect(result).toBe(mockToken); - }); - - it('should invalidate refresh token', async () => { - await service.invalidateRefreshToken('t1'); - expect(refreshTokenRepo.update).toHaveBeenCalledWith( - { token: 't1' }, - { isActive: false }, - ); - }); - }); -}); diff --git a/apps/backend/src/modules/user/user.service.ts b/apps/backend/src/modules/user/user.service.ts deleted file mode 100644 index e4381a71..00000000 --- a/apps/backend/src/modules/user/user.service.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { User } from './entities/user.entity'; -import { RefreshToken } from './entities/refresh-token.entity'; - -@Injectable() -export class UserService { - constructor( - @InjectRepository(User) - private userRepository: Repository, - @InjectRepository(RefreshToken) - private refreshTokenRepository: Repository, - ) {} - - async findByWalletAddress(walletAddress: string): Promise { - return this.userRepository.findOne({ where: { walletAddress } }); - } - - async findById(id: string): Promise { - return this.userRepository.findOne({ where: { id, isActive: true } }); - } - - async create(userData: Partial): Promise { - const user = this.userRepository.create(userData); - return this.userRepository.save(user); - } - - async update(id: string, userData: Partial): Promise { - await this.userRepository.update(id, userData); - const user = await this.findById(id); - if (!user) { - throw new Error('User not found'); - } - return user; - } - - async save(user: User): Promise { - return this.userRepository.save(user); - } - - async createRefreshToken( - tokenData: Partial, - ): Promise { - const refreshToken = this.refreshTokenRepository.create(tokenData); - return this.refreshTokenRepository.save(refreshToken); - } - - async findRefreshToken(token: string): Promise { - return this.refreshTokenRepository.findOne({ - where: { token, isActive: true }, - relations: ['user'], - }); - } - - async invalidateRefreshToken(token: string): Promise { - await this.refreshTokenRepository.update({ token }, { isActive: false }); - } -} diff --git a/apps/backend/src/modules/webhook/entities/webhook-delivery.entity.ts b/apps/backend/src/modules/webhook/entities/webhook-delivery.entity.ts deleted file mode 100644 index f6d3a9d9..00000000 --- a/apps/backend/src/modules/webhook/entities/webhook-delivery.entity.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - Column, - Entity, - PrimaryGeneratedColumn, - ManyToOne, - CreateDateColumn, - UpdateDateColumn, - Index, -} from 'typeorm'; -import { Webhook } from '../webhook.entity'; -import { WebhookDeliveryStatus } from '../../../types/webhook/webhook.types'; - -@Entity('webhook_delivery') -@Index(['status', 'nextRetryAt']) -export class WebhookDelivery { - @PrimaryGeneratedColumn('uuid') - id!: string; - - @ManyToOne(() => Webhook, { onDelete: 'CASCADE' }) - webhook!: Webhook; - - @Column() - webhookId!: string; - - @Column() - event!: string; - - @Column({ type: 'simple-json' }) - payload!: Record; - - @Column({ - type: 'simple-enum', - enum: WebhookDeliveryStatus, - default: WebhookDeliveryStatus.PENDING, - }) - status!: WebhookDeliveryStatus; - - @Column({ default: 1 }) - attempt!: number; - - @Column({ default: 5 }) - maxAttempts!: number; - - @Column({ type: 'datetime', nullable: true }) - nextRetryAt!: Date | null; - - @Column({ nullable: true }) - lastStatusCode!: number | null; - - @Column({ type: 'text', nullable: true }) - lastError!: string | null; - - @Column({ type: 'datetime', nullable: true }) - lastAttemptAt!: Date | null; - - @CreateDateColumn() - createdAt!: Date; - - @UpdateDateColumn() - updatedAt!: Date; -} diff --git a/apps/backend/src/modules/webhook/migrations/1670000000000-CreateWebhookTable.ts b/apps/backend/src/modules/webhook/migrations/1670000000000-CreateWebhookTable.ts deleted file mode 100644 index 17ed1ad2..00000000 --- a/apps/backend/src/modules/webhook/migrations/1670000000000-CreateWebhookTable.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class CreateWebhookTable1670000000000 implements MigrationInterface { - name = 'CreateWebhookTable1670000000000'; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE TABLE "webhooks" ( - "id" uuid PRIMARY KEY DEFAULT uuid_generate_v4(), - "url" varchar NOT NULL, - "secret" varchar NOT NULL, - "events" text NOT NULL, - "isActive" boolean DEFAULT true, - "userId" uuid NOT NULL, - "createdAt" TIMESTAMP DEFAULT now(), - "updatedAt" TIMESTAMP DEFAULT now(), - CONSTRAINT "FK_webhook_user" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE - ) - `); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query('DROP TABLE "webhooks"'); - } -} diff --git a/apps/backend/src/modules/webhook/webhook.controller.ts b/apps/backend/src/modules/webhook/webhook.controller.ts deleted file mode 100644 index fa11b0ff..00000000 --- a/apps/backend/src/modules/webhook/webhook.controller.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { - Controller, - Post, - Get, - Delete, - Body, - Param, - Req, - UseGuards, - UseInterceptors, -} from '@nestjs/common'; -import { WebhookService } from '../../services/webhook/webhook.service'; -import { WebhookEvent } from '../../types/webhook/webhook.types'; -import { AuthGuard } from '../auth/middleware/auth.guard'; -import { ThrottlerGuard } from '@nestjs/throttler'; - -class CreateWebhookDto { - url: string; - secret: string; - events: WebhookEvent[]; -} - -@Controller('webhooks') -@UseGuards(AuthGuard) -export class WebhookController { - constructor(private readonly webhookService: WebhookService) {} - - @Post() - @UseInterceptors(ThrottlerGuard) - async create( - @Req() req: { user: { id: string } }, - @Body() dto: CreateWebhookDto, - ) { - const userId = req?.user?.id; - if (!userId) throw new Error('User ID missing'); - return this.webhookService.createWebhook( - userId, - dto.url, - dto.secret, - dto.events, - ); - } - - @Get() - async list(@Req() req: { user: { id: string } }) { - const userId = req?.user?.id; - if (!userId) throw new Error('User ID missing'); - return this.webhookService.getUserWebhooks(userId); - } - - @Delete(':id') - async remove(@Req() req: { user: { id: string } }, @Param('id') id: string) { - const userId = req?.user?.id; - if (!userId) throw new Error('User ID missing'); - await this.webhookService.deleteWebhook(userId, id); - return { success: true }; - } -} diff --git a/apps/backend/src/modules/webhook/webhook.entity.ts b/apps/backend/src/modules/webhook/webhook.entity.ts deleted file mode 100644 index d963f1d5..00000000 --- a/apps/backend/src/modules/webhook/webhook.entity.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - Column, - Entity, - PrimaryGeneratedColumn, - ManyToOne, - CreateDateColumn, - UpdateDateColumn, -} from 'typeorm'; -import { User } from '../user/entities/user.entity'; -import type { WebhookEvent } from '../../types/webhook/webhook.types'; - -@Entity('webhooks') -export class Webhook { - @PrimaryGeneratedColumn('uuid') - id!: string; - - @Column() - url!: string; - - @Column() - secret!: string; - - @Column('simple-array') - events!: WebhookEvent[]; - - @Column({ default: true }) - isActive!: boolean; - - @ManyToOne(() => User, { nullable: false }) - user!: User; - - @CreateDateColumn() - createdAt!: Date; - - @UpdateDateColumn() - updatedAt!: Date; -} diff --git a/apps/backend/src/modules/webhook/webhook.module.ts b/apps/backend/src/modules/webhook/webhook.module.ts deleted file mode 100644 index a395b1c5..00000000 --- a/apps/backend/src/modules/webhook/webhook.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { AuthModule } from '../auth/auth.module'; -import { Webhook } from './webhook.entity'; -import { WebhookDelivery } from './entities/webhook-delivery.entity'; -import { WebhookService } from '../../services/webhook/webhook.service'; -import { WebhookController } from './webhook.controller'; - -@Module({ - imports: [TypeOrmModule.forFeature([Webhook, WebhookDelivery]), AuthModule], - providers: [WebhookService], - controllers: [WebhookController], - exports: [WebhookService], -}) -export class WebhookModule {} diff --git a/apps/backend/src/notifications/dto/create-notification.dto.ts b/apps/backend/src/notifications/dto/create-notification.dto.ts deleted file mode 100644 index 98ca4791..00000000 --- a/apps/backend/src/notifications/dto/create-notification.dto.ts +++ /dev/null @@ -1 +0,0 @@ -export class CreateNotificationDto {} diff --git a/apps/backend/src/notifications/dto/update-notification.dto.ts b/apps/backend/src/notifications/dto/update-notification.dto.ts deleted file mode 100644 index df935170..00000000 --- a/apps/backend/src/notifications/dto/update-notification.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateNotificationDto } from './create-notification.dto'; - -export class UpdateNotificationDto extends PartialType(CreateNotificationDto) {} diff --git a/apps/backend/src/notifications/entities/notification-preference.entity.ts b/apps/backend/src/notifications/entities/notification-preference.entity.ts deleted file mode 100644 index 24c34790..00000000 --- a/apps/backend/src/notifications/entities/notification-preference.entity.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - Column, - CreateDateColumn, - Entity, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from 'typeorm'; -import { - NotificationChannel, - NotificationEventType, -} from '../enums/notification-event.enum'; - -@Entity() -export class NotificationPreference { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - userId: string; - - @Column({ type: 'simple-enum', enum: NotificationChannel }) - channel: NotificationChannel; - - @Column({ default: true }) - enabled: boolean; - - @Column('simple-array') - eventTypes: NotificationEventType[]; - - @CreateDateColumn() - createdAt: Date; - - @UpdateDateColumn() - updatedAt: Date; -} diff --git a/apps/backend/src/notifications/entities/notification.entity.ts b/apps/backend/src/notifications/entities/notification.entity.ts deleted file mode 100644 index c104ac4b..00000000 --- a/apps/backend/src/notifications/entities/notification.entity.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - Column, - CreateDateColumn, - Entity, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from 'typeorm'; -import { - NotificationEventType, - NotificationStatus, -} from '../enums/notification-event.enum'; - -@Entity() -export class Notification { - @PrimaryGeneratedColumn('uuid') - id: string; - - @Column() - userId: string; - - @Column({ nullable: true }) - escrowId?: string; - - @Column({ type: 'simple-enum', enum: NotificationEventType }) - eventType: NotificationEventType; - - @Column({ type: 'simple-json' }) - payload: Record; - - @Column({ - type: 'simple-enum', - enum: NotificationStatus, - default: NotificationStatus.PENDING, - }) - status: NotificationStatus; - - @Column({ default: 0 }) - retryCount: number; - - @Column({ type: 'datetime', nullable: true }) - readAt?: Date; - - @CreateDateColumn() - createdAt: Date; - - @UpdateDateColumn() - updatedAt: Date; -} diff --git a/apps/backend/src/notifications/entities/update-preferences.dto.ts b/apps/backend/src/notifications/entities/update-preferences.dto.ts deleted file mode 100644 index bac19087..00000000 --- a/apps/backend/src/notifications/entities/update-preferences.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { IsEnum, IsBoolean, IsArray, ArrayNotEmpty } from 'class-validator'; -import { NotificationChannel } from '../enums/notification-event.enum'; -import { NotificationEventType } from '../enums/notification-event.enum'; - -export class UpdatePreferencesDto { - @IsEnum(NotificationChannel) - channel: NotificationChannel; - - @IsBoolean() - enabled: boolean; - - @IsArray() - @ArrayNotEmpty() - @IsEnum(NotificationEventType, { each: true }) - eventTypes: NotificationEventType[]; -} diff --git a/apps/backend/src/notifications/enums/notification-event.enum.ts b/apps/backend/src/notifications/enums/notification-event.enum.ts deleted file mode 100644 index 3c60ec15..00000000 --- a/apps/backend/src/notifications/enums/notification-event.enum.ts +++ /dev/null @@ -1,27 +0,0 @@ -export enum NotificationEventType { - PARTY_INVITED = 'PARTY_INVITED', - PARTY_ACCEPTED = 'PARTY_ACCEPTED', - PARTY_REJECTED = 'PARTY_REJECTED', - ESCROW_CREATED = 'ESCROW_CREATED', - ESCROW_FUNDED = 'ESCROW_FUNDED', - MILESTONE_RELEASED = 'MILESTONE_RELEASED', - ESCROW_COMPLETED = 'ESCROW_COMPLETED', - ESCROW_CANCELLED = 'ESCROW_CANCELLED', - DISPUTE_RAISED = 'DISPUTE_RAISED', - DISPUTE_RESOLVED = 'DISPUTE_RESOLVED', - ESCROW_EXPIRED = 'ESCROW_EXPIRED', - CONDITION_FULFILLED = 'CONDITION_FULFILLED', - CONDITION_CONFIRMED = 'CONDITION_CONFIRMED', - EXPIRATION_WARNING = 'EXPIRATION_WARNING', -} - -export enum NotificationStatus { - PENDING = 'pending', - SENT = 'sent', - FAILED = 'failed', -} - -export enum NotificationChannel { - EMAIL = 'email', - WEBHOOK = 'webhook', -} diff --git a/apps/backend/src/notifications/interface/notification-sender.interface.ts b/apps/backend/src/notifications/interface/notification-sender.interface.ts deleted file mode 100644 index 91ca1b84..00000000 --- a/apps/backend/src/notifications/interface/notification-sender.interface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Notification } from '../entities/notification.entity'; -import { NotificationChannel } from '../enums/notification-event.enum'; - -export interface NotificationSender { - channel: NotificationChannel; - send(notification: Notification): Promise; -} diff --git a/apps/backend/src/notifications/notifications.controller.ts b/apps/backend/src/notifications/notifications.controller.ts deleted file mode 100644 index dccc0d50..00000000 --- a/apps/backend/src/notifications/notifications.controller.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { AuthGuard } from '../modules/auth/middleware/auth.guard'; -import { NotificationService } from './notifications.service'; -import { PreferenceService } from './preference.service'; -import { - Body, - Controller, - Get, - Put, - Req, - UseGuards, - Post, -} from '@nestjs/common'; -import { Request } from 'express'; -import { UpdatePreferencesDto } from './entities/update-preferences.dto'; - -interface AuthenticatedRequest extends Request { - user: { - id: string; - }; -} - -@Controller('notifications') -@UseGuards(AuthGuard) -export class NotificationController { - constructor( - private preferenceService: PreferenceService, - private notificationService: NotificationService, - ) {} - - @Get('preferences') - getPreferences(@Req() req: AuthenticatedRequest) { - return this.preferenceService.getUserPreferences(req.user.id); - } - - @Put('preferences') - updatePreferences( - @Req() req: AuthenticatedRequest, - @Body() dto: UpdatePreferencesDto[], - ) { - return this.preferenceService.updatePreferences(req.user.id, dto); - } - - @Get() - getNotifications(@Req() req: AuthenticatedRequest) { - return this.notificationService.getUserNotifications(req.user.id); - } - - @Get('unread-count') - getUnreadCount(@Req() req: AuthenticatedRequest) { - return this.notificationService.getUnreadCount(req.user.id); - } - - @Post('mark-as-read') - async markAsRead( - @Req() req: AuthenticatedRequest, - @Body('notificationId') notificationId?: string, - ) { - return this.notificationService.markAsRead(req.user.id, notificationId); - } -} diff --git a/apps/backend/src/notifications/notifications.module.ts b/apps/backend/src/notifications/notifications.module.ts deleted file mode 100644 index 58852261..00000000 --- a/apps/backend/src/notifications/notifications.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { ConfigModule } from '@nestjs/config'; -import { AuthModule } from '../modules/auth/auth.module'; -import { Notification } from './entities/notification.entity'; -import { NotificationPreference } from './entities/notification-preference.entity'; -import { NotificationController } from './notifications.controller'; -import { NotificationService } from './notifications.service'; -import { PreferenceService } from './preference.service'; -import { EmailSender } from './senders/email.sender'; -import { WebhookSender } from './senders/webhook.sender'; - -@Module({ - imports: [ - ConfigModule, - AuthModule, - TypeOrmModule.forFeature([Notification, NotificationPreference]), - ], - controllers: [NotificationController], - providers: [ - NotificationService, - PreferenceService, - EmailSender, - WebhookSender, - ], - exports: [NotificationService, PreferenceService], -}) -export class NotificationsModule {} diff --git a/apps/backend/src/notifications/notifications.service.spec.ts b/apps/backend/src/notifications/notifications.service.spec.ts deleted file mode 100644 index d0dd234d..00000000 --- a/apps/backend/src/notifications/notifications.service.spec.ts +++ /dev/null @@ -1,174 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { NotificationService } from './notifications.service'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { Notification } from './entities/notification.entity'; -import { PreferenceService } from './preference.service'; -import { EmailSender } from './senders/email.sender'; -import { WebhookSender } from './senders/webhook.sender'; -import { - NotificationStatus, - NotificationChannel, - NotificationEventType, -} from './enums/notification-event.enum'; -import { Repository } from 'typeorm'; - -describe('NotificationService', () => { - let service: NotificationService; - let repo: jest.Mocked>; - let preferenceService: jest.Mocked; - let emailSender: jest.Mocked; - let webhookSender: jest.Mocked; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - NotificationService, - { - provide: getRepositoryToken(Notification), - useValue: { - find: jest.fn(), - findOne: jest.fn(), - create: jest.fn(), - save: jest.fn(), - update: jest.fn(), - count: jest.fn(), - }, - }, - { - provide: PreferenceService, - useValue: { - getUserPreferences: jest.fn(), - }, - }, - { - provide: EmailSender, - useValue: { - send: jest.fn(), - }, - }, - { - provide: WebhookSender, - useValue: { - send: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(NotificationService); - repo = module.get(getRepositoryToken(Notification)); - preferenceService = module.get(PreferenceService); - emailSender = module.get(EmailSender); - webhookSender = module.get(WebhookSender); - }); - - const mockPref = { - userId: 'u1', - channel: NotificationChannel.EMAIL, - enabled: true, - eventTypes: [NotificationEventType.ESCROW_CREATED], - }; - - const mockNotification = { - id: 'n1', - userId: 'u1', - eventType: NotificationEventType.ESCROW_CREATED, - payload: { escrowId: 'e1' }, - status: NotificationStatus.PENDING, - retryCount: 0, - readAt: null as Date | null, - save: jest.fn(), - }; - - describe('handleEscrowEvent', () => { - it('should create notification if preferences enabled', async () => { - preferenceService.getUserPreferences.mockResolvedValue([mockPref] as any); - repo.create.mockReturnValue(mockNotification as any); - - await service.handleEscrowEvent( - 'u1', - NotificationEventType.ESCROW_CREATED, - { escrowId: 'e1' }, - ); - - expect(repo.save).toHaveBeenCalled(); - }); - - it('should skip if preference disabled', async () => { - preferenceService.getUserPreferences.mockResolvedValue([ - { ...mockPref, enabled: false }, - ] as any); - - await service.handleEscrowEvent( - 'u1', - NotificationEventType.ESCROW_CREATED, - { escrowId: 'e1' }, - ); - - expect(repo.save).not.toHaveBeenCalled(); - }); - }); - - describe('processPendingNotifications', () => { - it('should process pending notifications and mark as sent', async () => { - repo.find.mockResolvedValue([mockNotification] as any); - preferenceService.getUserPreferences.mockResolvedValue([mockPref] as any); - emailSender.send.mockResolvedValue(); - - await service.processPendingNotifications(); - - expect(emailSender.send).toHaveBeenCalledWith(mockNotification); - expect(mockNotification.status).toBe(NotificationStatus.SENT); - expect(repo.save).toHaveBeenCalledWith(mockNotification); - }); - - it('should increment retryCount on failure', async () => { - repo.find.mockResolvedValue([mockNotification] as any); - preferenceService.getUserPreferences.mockResolvedValue([mockPref] as any); - emailSender.send.mockRejectedValue(new Error('Send error')); - - await service.processPendingNotifications(); - - expect(mockNotification.retryCount).toBe(1); - expect(mockNotification.status).toBe(NotificationStatus.PENDING); - }); - - it('should mark as failed if retry count exceeded', async () => { - const failingNotification = { ...mockNotification, retryCount: 4 }; - repo.find.mockResolvedValue([failingNotification] as any); - preferenceService.getUserPreferences.mockResolvedValue([mockPref] as any); - emailSender.send.mockRejectedValue(new Error('Send error')); - - await service.processPendingNotifications(); - - expect(failingNotification.status).toBe(NotificationStatus.FAILED); - }); - }); - - describe('markAsRead', () => { - it('should mark single notification as read', async () => { - repo.findOne.mockResolvedValue(mockNotification as any); - await service.markAsRead('u1', 'n1'); - expect(mockNotification.readAt).toBeDefined(); - expect(repo.save).toHaveBeenCalled(); - }); - - it('should throw if notification not found', async () => { - repo.findOne.mockResolvedValue(null); - await expect(service.markAsRead('u1', 'n1')).rejects.toThrow(); - }); - - it('should mark all as read if no id provided', async () => { - await service.markAsRead('u1'); - expect(repo.update).toHaveBeenCalled(); - }); - }); - - describe('getUnreadCount', () => { - it('should return count', async () => { - repo.count.mockResolvedValue(5); - const result = await service.getUnreadCount('u1'); - expect(result).toBe(5); - }); - }); -}); diff --git a/apps/backend/src/notifications/notifications.service.ts b/apps/backend/src/notifications/notifications.service.ts deleted file mode 100644 index 14777293..00000000 --- a/apps/backend/src/notifications/notifications.service.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { Cron, CronExpression } from '@nestjs/schedule'; -import { - NotificationChannel, - NotificationEventType, - NotificationStatus, -} from './enums/notification-event.enum'; -import { NotificationSender } from './interface/notification-sender.interface'; -import { Notification } from './entities/notification.entity'; -import { InjectRepository } from '@nestjs/typeorm'; -import { WebhookSender } from './senders/webhook.sender'; -import { Repository, IsNull } from 'typeorm'; -import { EmailSender } from './senders/email.sender'; -import { PreferenceService } from './preference.service'; - -@Injectable() -export class NotificationService { - private readonly logger = new Logger(NotificationService.name); - private senders: Map; - - constructor( - @InjectRepository(Notification) - private repo: Repository, - private preferenceService: PreferenceService, - emailSender: EmailSender, - webhookSender: WebhookSender, - ) { - this.senders = new Map([ - [NotificationChannel.EMAIL, emailSender], - [NotificationChannel.WEBHOOK, webhookSender], - ]); - } - - async handleEscrowEvent( - userId: string, - eventType: NotificationEventType, - payload: Record, - ) { - const prefs = await this.preferenceService.getUserPreferences(userId); - - for (const pref of prefs) { - if (!pref.enabled) continue; - if (!pref.eventTypes.includes(eventType)) continue; - - await this.repo.save( - this.repo.create({ - userId, - eventType, - payload, - escrowId: (payload.escrowId as string) || undefined, - status: NotificationStatus.PENDING, - }), - ); - } - } - - @Cron(CronExpression.EVERY_30_SECONDS) - async processPendingNotifications() { - this.logger.debug('Starting pending notifications processing'); - const pending = await this.repo.find({ - where: { status: NotificationStatus.PENDING }, - take: 50, - }); - - for (const notification of pending) { - try { - const prefs = await this.preferenceService.getUserPreferences( - notification.userId, - ); - - for (const pref of prefs) { - if (!pref.enabled) continue; - if (!pref.eventTypes.includes(notification.eventType)) continue; - - const sender = this.senders.get(pref.channel); - if (!sender) continue; - - await sender.send(notification); - } - - notification.status = NotificationStatus.SENT; - } catch (error) { - notification.retryCount += 1; - notification.status = - notification.retryCount > 3 - ? NotificationStatus.FAILED - : NotificationStatus.PENDING; - this.logger.error( - `Failed to process notification ${notification.id}; retryCount=${notification.retryCount}`, - error instanceof Error ? error.stack : String(error), - ); - } - - await this.repo.save(notification); - } - } - - async getUserNotifications(userId: string) { - return this.repo.find({ - where: { userId }, - order: { createdAt: 'DESC' }, - take: 50, - }); - } - - async markAsRead(userId: string, notificationId?: string) { - if (notificationId) { - const notification = await this.repo.findOne({ - where: { id: notificationId, userId }, - }); - if (!notification) { - throw new Error('Notification not found'); - } - notification.readAt = new Date(); - await this.repo.save(notification); - return notification; - } else { - // Mark all as read - await this.repo.update( - { userId, readAt: IsNull() }, - { readAt: new Date() }, - ); - return { success: true }; - } - } - - async getUnreadCount(userId: string): Promise { - return this.repo.count({ - where: { userId, readAt: IsNull() }, - }); - } -} diff --git a/apps/backend/src/notifications/preference.service.spec.ts b/apps/backend/src/notifications/preference.service.spec.ts deleted file mode 100644 index 63e183ee..00000000 --- a/apps/backend/src/notifications/preference.service.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { PreferenceService } from './preference.service'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { NotificationPreference } from './entities/notification-preference.entity'; -import { Repository } from 'typeorm'; -import { NotificationChannel } from './enums/notification-event.enum'; - -describe('PreferenceService', () => { - let service: PreferenceService; - let repo: jest.Mocked>; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - PreferenceService, - { - provide: getRepositoryToken(NotificationPreference), - useValue: { - find: jest.fn(), - findOne: jest.fn(), - create: jest.fn(), - save: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(PreferenceService); - repo = module.get(getRepositoryToken(NotificationPreference)); - }); - - const mockPref = { - userId: 'u1', - channel: NotificationChannel.EMAIL, - enabled: true, - eventTypes: [], - }; - - describe('getUserPreferences', () => { - it('should return preferences for a user', async () => { - repo.find.mockResolvedValue([mockPref] as any); - const result = await service.getUserPreferences('u1'); - expect(repo.find).toHaveBeenCalledWith({ where: { userId: 'u1' } }); - expect(result).toEqual([mockPref]); - }); - }); - - describe('updatePreferences', () => { - it('should create new preference if not exists', async () => { - repo.findOne.mockResolvedValue(null); - repo.create.mockReturnValue(mockPref as any); - repo.save.mockResolvedValue(mockPref as any); - - const updates = [ - { channel: NotificationChannel.EMAIL, enabled: true, eventTypes: [] }, - ]; - const result = await service.updatePreferences('u1', updates as any); - - expect(repo.create).toHaveBeenCalled(); - expect(repo.save).toHaveBeenCalled(); - expect(result).toEqual([mockPref]); - }); - - it('should update existing preference', async () => { - const existing = { ...mockPref, enabled: false }; - repo.findOne.mockResolvedValue(existing as any); - repo.save.mockResolvedValue({ ...existing, enabled: true } as any); - - const updates = [ - { channel: NotificationChannel.EMAIL, enabled: true, eventTypes: [] }, - ]; - const result = await service.updatePreferences('u1', updates as any); - - expect(repo.save).toHaveBeenCalled(); - expect(result[0].enabled).toBe(true); - }); - }); -}); diff --git a/apps/backend/src/notifications/preference.service.ts b/apps/backend/src/notifications/preference.service.ts deleted file mode 100644 index 5a9642aa..00000000 --- a/apps/backend/src/notifications/preference.service.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { NotificationPreference } from './entities/notification-preference.entity'; -import { Repository } from 'typeorm'; -import { UpdatePreferencesDto } from './entities/update-preferences.dto'; - -@Injectable() -export class PreferenceService { - constructor( - @InjectRepository(NotificationPreference) - private repo: Repository, - ) {} - - async getUserPreferences(userId: string) { - return this.repo.find({ where: { userId } }); - } - - async updatePreferences( - userId: string, - updates: UpdatePreferencesDto[], - ): Promise { - const results: NotificationPreference[] = []; - - for (const update of updates) { - let pref = await this.repo.findOne({ - where: { userId, channel: update.channel }, - }); - - if (!pref) { - pref = this.repo.create({ - userId, - channel: update.channel, - enabled: update.enabled, - eventTypes: update.eventTypes, - }); - } else { - pref.enabled = update.enabled; - pref.eventTypes = update.eventTypes; - } - - const saved = await this.repo.save(pref); - results.push(saved); - } - - return results; - } -} diff --git a/apps/backend/src/notifications/senders/email.sender.ts b/apps/backend/src/notifications/senders/email.sender.ts deleted file mode 100644 index 07456daf..00000000 --- a/apps/backend/src/notifications/senders/email.sender.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { ConfigService } from '@nestjs/config'; -import { createTransport, Transporter } from 'nodemailer'; -import { - NotificationChannel, - NotificationEventType, -} from '../enums/notification-event.enum'; -import { NotificationSender } from '../interface/notification-sender.interface'; -import { Notification } from '../entities/notification.entity'; - -@Injectable() -export class EmailSender implements NotificationSender { - private readonly logger = new Logger(EmailSender.name); - private readonly transporter: Transporter; - private readonly fromAddress: string; - channel = NotificationChannel.EMAIL; - - constructor(private readonly configService: ConfigService) { - const host = this.configService.get('SMTP_HOST'); - const port = Number(this.configService.get('SMTP_PORT', '587')); - const user = this.configService.get('SMTP_USER'); - const pass = this.configService.get('SMTP_PASS'); - - this.fromAddress = this.configService.get( - 'EMAIL_FROM', - 'no-reply@vaultix.local', - ); - - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - this.transporter = createTransport({ - host, - port, - secure: port === 465, - auth: user && pass ? { user, pass } : undefined, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any); - } - - async send(notification: Notification): Promise { - const to = this.resolveRecipient(notification.payload); - if (!to) { - throw new Error( - `Missing recipient email for notification ${notification.id}`, - ); - } - - const template = this.buildEmailTemplate(notification); - - try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any - await (this.transporter as any).sendMail({ - from: this.fromAddress, - to, - subject: template.subject, - text: template.textBody, - html: template.htmlBody, - }); - } catch (error) { - this.logger.error( - `Failed to send email for notification ${notification.id}`, - error instanceof Error ? error.stack : String(error), - ); - throw error; - } - } - - private resolveRecipient(payload: Record): string | null { - const candidateKeys = [ - 'email', - 'userEmail', - 'recipientEmail', - 'to', - 'buyerEmail', - 'sellerEmail', - ]; - - for (const key of candidateKeys) { - const value = payload[key]; - if (typeof value === 'string' && value.trim().length > 0) { - return value.trim(); - } - } - - return null; - } - - private buildEmailTemplate(notification: Notification): { - subject: string; - textBody: string; - htmlBody: string; - } { - const payload = notification.payload; - const event = notification.eventType; - const escrowId = this.readString(payload, 'escrowId') ?? 'unknown escrow'; - const escrowTitle = this.readString(payload, 'escrowTitle') ?? 'Escrow'; - const amount = this.readString(payload, 'amount'); - const asset = this.readString(payload, 'asset') ?? 'asset'; - const actionUrl = this.readString(payload, 'actionUrl'); - const disputeId = this.readString(payload, 'disputeId'); - const condition = this.readString(payload, 'condition') ?? 'A condition'; - const expiresAt = this.readString(payload, 'expiresAt'); - - const role = this.readString(payload, 'role') ?? 'party'; - - const subjects: Record = { - [NotificationEventType.PARTY_INVITED]: `You have been invited to escrow: ${escrowTitle}`, - [NotificationEventType.PARTY_ACCEPTED]: `Party accepted invitation for escrow ${escrowId}`, - [NotificationEventType.PARTY_REJECTED]: `Party rejected invitation for escrow ${escrowId}`, - [NotificationEventType.ESCROW_CREATED]: `Escrow created: ${escrowTitle} (${escrowId})`, - [NotificationEventType.ESCROW_FUNDED]: `Escrow funded: ${escrowTitle} (${escrowId})`, - [NotificationEventType.MILESTONE_RELEASED]: `Milestone released for escrow ${escrowId}`, - [NotificationEventType.ESCROW_COMPLETED]: `Escrow completed: ${escrowTitle} (${escrowId})`, - [NotificationEventType.ESCROW_CANCELLED]: `Escrow cancelled: ${escrowTitle} (${escrowId})`, - [NotificationEventType.DISPUTE_RAISED]: `Dispute filed for escrow ${escrowId}`, - [NotificationEventType.DISPUTE_RESOLVED]: `Dispute resolved for escrow ${escrowId}`, - [NotificationEventType.ESCROW_EXPIRED]: `Escrow expired: ${escrowId}`, - [NotificationEventType.CONDITION_FULFILLED]: `Condition fulfilled for escrow ${escrowId}`, - [NotificationEventType.CONDITION_CONFIRMED]: `Condition confirmed for escrow ${escrowId}`, - [NotificationEventType.EXPIRATION_WARNING]: `Escrow expiring in 24h: ${escrowId}`, - }; - - const textByEvent: Record = { - [NotificationEventType.PARTY_INVITED]: - `You have been invited to participate as ${role} in escrow "${escrowTitle}" (${escrowId}).` + - this.optionalAmount(amount, asset) + - (actionUrl ? `` : ' Log in to accept or reject the invitation.'), - [NotificationEventType.PARTY_ACCEPTED]: `A party has accepted their ${role} invitation for escrow ${escrowId}.`, - [NotificationEventType.PARTY_REJECTED]: `A party has rejected their ${role} invitation for escrow ${escrowId}.`, - [NotificationEventType.ESCROW_CREATED]: - `A new escrow (${escrowId}) has been created.` + - this.optionalAmount(amount, asset), - [NotificationEventType.ESCROW_FUNDED]: - `Escrow ${escrowId} has been funded.` + - this.optionalAmount(amount, asset), - [NotificationEventType.MILESTONE_RELEASED]: `A milestone has been released for escrow ${escrowId}.`, - [NotificationEventType.ESCROW_COMPLETED]: `Escrow ${escrowId} is now completed.`, - [NotificationEventType.ESCROW_CANCELLED]: `Escrow ${escrowId} has been cancelled.`, - [NotificationEventType.DISPUTE_RAISED]: `A dispute (${disputeId ?? 'unknown'}) has been filed for escrow ${escrowId}.`, - [NotificationEventType.DISPUTE_RESOLVED]: `Dispute (${disputeId ?? 'unknown'}) has been resolved for escrow ${escrowId}.`, - [NotificationEventType.ESCROW_EXPIRED]: `Escrow ${escrowId} has expired.`, - [NotificationEventType.CONDITION_FULFILLED]: `${condition} has been fulfilled for escrow ${escrowId}.`, - [NotificationEventType.CONDITION_CONFIRMED]: `${condition} has been confirmed for escrow ${escrowId}.`, - [NotificationEventType.EXPIRATION_WARNING]: - `Escrow ${escrowId} will expire in approximately 24 hours` + - (expiresAt ? ` (at ${expiresAt}).` : '.'), - }; - - const actionLine = actionUrl ? `\n\nReview details: ${actionUrl}` : ''; - const textBody = - `${textByEvent[event]}\n\nNotification ID: ${notification.id}${actionLine}`.trim(); - const htmlBody = - `

${textByEvent[event]}

` + - (actionUrl - ? `

Review escrow details

` - : '') + - `

Notification ID: ${notification.id}

`; - - return { - subject: subjects[event], - textBody, - htmlBody, - }; - } - - private readString(payload: Record, key: string) { - const value = payload[key]; - if (typeof value !== 'string') return null; - const trimmed = value.trim(); - return trimmed.length > 0 ? trimmed : null; - } - - private optionalAmount(amount: string | null, asset: string): string { - if (!amount) return ''; - return ` Amount: ${amount} ${asset}.`; - } -} diff --git a/apps/backend/src/notifications/senders/webhook.sender.ts b/apps/backend/src/notifications/senders/webhook.sender.ts deleted file mode 100644 index c9795ab9..00000000 --- a/apps/backend/src/notifications/senders/webhook.sender.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { NotificationChannel } from '../enums/notification-event.enum'; -import { NotificationSender } from '../interface/notification-sender.interface'; -import { Notification } from '../entities/notification.entity'; - -@Injectable() -export class WebhookSender implements NotificationSender { - channel = NotificationChannel.WEBHOOK; - - send(notification: Notification): Promise { - console.log('Sending webhook:', notification.id); - return Promise.resolve(); - } -} diff --git a/apps/backend/src/scripts/admin-seed.ts b/apps/backend/src/scripts/admin-seed.ts deleted file mode 100644 index 7f5f2eab..00000000 --- a/apps/backend/src/scripts/admin-seed.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { NestFactory } from '@nestjs/core'; -import { AppModule } from '../app.module'; -import { User, UserRole } from '../modules/user/entities/user.entity'; -import { getRepository } from 'typeorm'; - -async function seedAdmin() { - const app = await NestFactory.createApplicationContext(AppModule); - - try { - const userRepository = getRepository(User); - - // Check if admin already exists - const existingAdmin = await userRepository.findOne({ - where: { walletAddress: 'ADMIN_WALLET_ADDRESS' }, - }); - - if (existingAdmin) { - console.log('Admin user already exists'); - return; - } - - // Create super admin - const superAdmin = userRepository.create({ - walletAddress: 'ADMIN_WALLET_ADDRESS', - role: UserRole.SUPER_ADMIN, - isActive: true, - }); - - await userRepository.save(superAdmin); - - // Create regular admin - const admin = userRepository.create({ - walletAddress: 'REGULAR_ADMIN_WALLET_ADDRESS', - role: UserRole.ADMIN, - isActive: true, - }); - - await userRepository.save(admin); - - console.log('Admin users created successfully'); - console.log('Super Admin:', superAdmin.walletAddress); - console.log('Admin:', admin.walletAddress); - } catch (error) { - console.error('Error seeding admin users:', error); - } finally { - await app.close(); - } -} - -void seedAdmin(); diff --git a/apps/backend/src/services/escrow.ts b/apps/backend/src/services/escrow.ts deleted file mode 100644 index 82515d47..00000000 --- a/apps/backend/src/services/escrow.ts +++ /dev/null @@ -1,31 +0,0 @@ -import api from '../clients/client'; - -interface EscrowPayload { - [key: string]: unknown; -} - -interface EscrowResponse { - [key: string]: unknown; -} - -export const createEscrow = async ( - payload: EscrowPayload, -): Promise => { - const response = await api.post('/escrow', payload); - return response.data; -}; - -export const getEscrows = async (): Promise => { - const response = await api.get('/escrow'); - return response.data; -}; - -export const getEscrowById = async (id: string): Promise => { - const response = await api.get(`/escrow/${id}`); - return response.data; -}; - -export const releaseEscrow = async (id: string): Promise => { - const response = await api.patch(`/escrow/${id}/release`); - return response.data; -}; diff --git a/apps/backend/src/services/stellar.service.spec.ts b/apps/backend/src/services/stellar.service.spec.ts deleted file mode 100644 index 35a0a196..00000000 --- a/apps/backend/src/services/stellar.service.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { ConfigModule } from '@nestjs/config'; -import * as StellarSdk from '@stellar/stellar-sdk'; -import stellarConfig from '../config/stellar.config'; -import { StellarService } from './stellar.service'; - -describe('StellarService', () => { - let service: StellarService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - imports: [ConfigModule.forFeature(stellarConfig)], - providers: [StellarService], - }).compile(); - - service = module.get(StellarService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); - - it('should validate public keys correctly', () => { - // Generate a valid keypair for testing - const keypair = StellarSdk.Keypair.random(); - const validPublicKey = keypair.publicKey(); - const invalidPublicKey = 'invalid-key'; - - expect(service.isValidPublicKey(validPublicKey)).toBe(true); - expect(service.isValidPublicKey(invalidPublicKey)).toBe(false); - }); - - it('should validate secret keys correctly', () => { - // Generate a valid keypair for testing - const keypair = StellarSdk.Keypair.random(); - const validSecretKey = keypair.secret(); - const invalidSecretKey = 'invalid-key'; - - expect(service.isValidSecretKey(validSecretKey)).toBe(true); - expect(service.isValidSecretKey(invalidSecretKey)).toBe(false); - }); - - it('should create a new keypair', () => { - const keypair = service.createKeypair(); - - expect(keypair).toBeDefined(); - expect(keypair.publicKey()).toBeDefined(); - expect(keypair.secret()).toBeDefined(); - }); - - // Mock tests for network operations - it('should build a transaction structure correctly', () => { - // This test validates the transaction building structure - expect(() => { - // Test structure validation - const testAmount = '10'; - const testAsset = StellarSdk.Asset.native(); - expect(testAmount).toBe('10'); - expect(testAsset).toBeDefined(); - }).not.toThrow(); - }); -}); diff --git a/apps/backend/src/services/stellar.service.ts b/apps/backend/src/services/stellar.service.ts index 0e31dc68..151297b6 100644 --- a/apps/backend/src/services/stellar.service.ts +++ b/apps/backend/src/services/stellar.service.ts @@ -9,19 +9,23 @@ import { StellarTransactionResponse, StellarServer, } from '../types/stellar.types'; +import { StellarRpcErrorHandler } from '../utils/stellar-rpc-error-handler.util'; @Injectable() export class StellarService { private readonly logger = new Logger(StellarService.name); private server: StellarServer; private networkPassphrase: string; + private errorHandler: StellarRpcErrorHandler; constructor( @Inject(stellarConfig.KEY) private config: ConfigType, + errorHandler: StellarRpcErrorHandler, ) { this.networkPassphrase = this.config.networkPassphrase; this.server = new StellarSdk.Horizon.Server(this.config.horizonUrl); + this.errorHandler = errorHandler; this.logger.log( `Initialized Stellar service for ${this.config.network} network`, @@ -35,15 +39,6 @@ export class StellarService { * @returns Account record with balance and sequence number */ - async checkHealth(): Promise { - try { - await this.server.root(); - return true; - } catch { - return false; - } - } - async getAccount(publicKey: string): Promise { try { this.logger.log(`Fetching account info for: ${publicKey}`); @@ -54,13 +49,13 @@ export class StellarService { .call()) as unknown as StellarAccountResponse; this.logger.log(`Successfully retrieved account info for: ${publicKey}`); - return account; } catch (error) { - this.logger.error( - `Failed to fetch account ${publicKey}: ${this.getErrorMessage(error)}`, + this.errorHandler.logError('getAccount', error, `publicKey: ${publicKey}`); + throw this.errorHandler.toStellarRpcException( + error, + `Error fetching account ${publicKey}`, ); - throw this.mapStellarError(error, `Error fetching account ${publicKey}`); } } @@ -79,9 +74,7 @@ export class StellarService { .call(); return assets.records.length > 0; } catch (error) { - this.logger.error( - `Failed to validate asset ${code}: ${this.getErrorMessage(error)}`, - ); + this.errorHandler.logError('validateAsset', error, `code: ${code}, issuer: ${issuer}`); return false; } } @@ -132,10 +125,8 @@ export class StellarService { return transaction; } catch (error) { - this.logger.error( - `Failed to build transaction for account ${sourcePublicKey}: ${this.getErrorMessage(error)}`, - ); - throw this.mapStellarError( + this.errorHandler.logError('buildTransaction', error, `sourcePublicKey: ${sourcePublicKey}`); + throw this.errorHandler.toStellarRpcException( error, `Error building transaction for account ${sourcePublicKey}`, ); @@ -162,15 +153,16 @@ export class StellarService { }, this.config.maxRetries, this.config.retryDelay, + this.config.retryFactor, ); + this.errorHandler.recordSuccess(); this.logger.log(`Successfully submitted transaction: ${result.hash}`); return result; } catch (error) { - this.logger.error( - `Failed to submit transaction after ${this.config.maxRetries + 1} attempts: ${this.getErrorMessage(error)}`, - ); - throw this.mapStellarError( + this.errorHandler.recordFailure(); + this.errorHandler.logError('submitTransaction', error, ''); + throw this.errorHandler.toStellarRpcException( error, `Error submitting transaction after ${this.config.maxRetries + 1} attempts`, ); @@ -199,7 +191,6 @@ export class StellarService { }; // Create event stream for account transactions - const eventSource = this.server .transactions() .forAccount(accountId) @@ -241,16 +232,28 @@ export class StellarService { return null; } - this.logger.error( - `Failed to check transaction status ${transactionHash}: ${this.getErrorMessage(error)}`, - ); - throw this.mapStellarError( + this.errorHandler.logError('checkTransactionStatus', error, `transactionHash: ${transactionHash}`); + throw this.errorHandler.toStellarRpcException( error, `Error checking transaction status ${transactionHash}`, ); } } + /** + * Checks the health of the Stellar Horizon service + * @returns True if Horizon is reachable, false otherwise + */ + async checkHealth(): Promise { + try { + await this.server.root(); + return true; + } catch (error) { + this.errorHandler.logError('checkHealth', error, ''); + return false; + } + } + /** * Validates a Stellar public key * @param publicKey The public key to validate @@ -305,77 +308,4 @@ export class StellarService { ); /* eslint-enable @typescript-eslint/no-unsafe-member-access */ } - - /** - * Safely extracts error message from unknown error type - */ - private getErrorMessage(error: unknown): string { - if (error instanceof Error) { - return error.message; - } - if (typeof error === 'object' && error !== null && 'message' in error) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return String((error as any).message); - } - return 'Unknown error'; - } - - /** - * Maps Stellar SDK errors to more descriptive error messages - * @param error The error to map - * @param defaultMessage Default message if specific mapping isn't found - * @returns Mapped error - */ - private mapStellarError(error: unknown, defaultMessage: string): Error { - if (!error) { - return new Error(defaultMessage); - } - - // Type guard for error objects - if (typeof error === 'object' && error !== null) { - // Check if it's a Horizon API error - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const errorObj = error as any; - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ - if (errorObj.response?.data) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const problem = errorObj.response.data; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const title = - problem.title || problem.extras?.result_codes?.transaction; - - if (problem.detail) { - return new Error( - `Stellar API Error: ${String(problem.detail)} (${String(title)})`, - ); - } - - if (problem.extras?.result_codes) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const codes = problem.extras.result_codes; - return new Error( - `Stellar Transaction Error: ${JSON.stringify(codes)}`, - ); - } - } - /* eslint-enable @typescript-eslint/no-unsafe-member-access */ - - // Check for specific Stellar SDK error types - // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access - if (errorObj.constructor?.name?.includes('NetworkError')) { - return new Error( - `Network Error: Failed to connect to Stellar network (${this.getErrorMessage(error)})`, - ); - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access - if (errorObj.constructor?.name?.includes('NotFoundError')) { - return new Error(`Not Found: ${this.getErrorMessage(error)}`); - } - - return new Error(`${defaultMessage}: ${this.getErrorMessage(error)}`); - } - - return new Error(defaultMessage); - } -} +} \ No newline at end of file diff --git a/apps/backend/src/services/stellar/escrow-operations.ts b/apps/backend/src/services/stellar/escrow-operations.ts deleted file mode 100644 index 563195e2..00000000 --- a/apps/backend/src/services/stellar/escrow-operations.ts +++ /dev/null @@ -1,306 +0,0 @@ -import * as StellarSdk from '@stellar/stellar-sdk'; -import { Injectable, Logger } from '@nestjs/common'; -import { normalizeMetadataHash } from '../../modules/escrow/utils/metadata-hash.util'; - -@Injectable() -export class EscrowOperationsService { - private readonly logger = new Logger(EscrowOperationsService.name); - - private readonly contractId: string; - - constructor() { - this.contractId = process.env.STELLAR_CONTRACT_ID || ''; - } - - /** - * Creates operations for initializing an escrow contract - */ - createEscrowInitializationOps( - escrowId: string, - depositorPublicKey: string, - recipientPublicKey: string, - tokenAddress: string, - milestones: Array<{ id: number; amount: string; description: string }>, - deadline: number, - metadataReference: string, - ): StellarSdk.xdr.Operation[] { - try { - this.logger.log( - `Creating escrow initialization ops for escrow ID: ${escrowId}`, - ); - - const contract = new StellarSdk.Contract(this.contractId); - const metadataHash = Buffer.from( - normalizeMetadataHash(metadataReference), - 'hex', - ); - - const milestoneVec = StellarSdk.xdr.ScVal.scvVec( - milestones.map((m) => - StellarSdk.xdr.ScVal.scvMap([ - new StellarSdk.xdr.ScMapEntry({ - key: StellarSdk.xdr.ScVal.scvSymbol('amount'), - val: StellarSdk.xdr.ScVal.scvI128( - new StellarSdk.xdr.Int128Parts({ - lo: new StellarSdk.xdr.Uint64(m.amount), - hi: new StellarSdk.xdr.Int64('0'), - }), - ), - }), - new StellarSdk.xdr.ScMapEntry({ - key: StellarSdk.xdr.ScVal.scvSymbol('description'), - val: StellarSdk.xdr.ScVal.scvSymbol( - m.description.replace(/\s+/g, '_'), - ), - }), - new StellarSdk.xdr.ScMapEntry({ - key: StellarSdk.xdr.ScVal.scvSymbol('status'), - val: StellarSdk.xdr.ScVal.scvSymbol('Pending'), - }), - ]), - ), - ); - - const op = contract.call( - 'create_escrow', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - new StellarSdk.Address(depositorPublicKey).toScVal(), - new StellarSdk.Address(recipientPublicKey).toScVal(), - new StellarSdk.Address( - tokenAddress === 'native' - ? 'CDLZFC3SYJYDZT7K67VZ75YJFCGSN5W4B77T2YI2EHCWH6I6D6LNCU6B' /* Native XLM Token Contract in Testnet */ - : tokenAddress, - ).toScVal(), - milestoneVec, - StellarSdk.xdr.ScVal.scvU64( - new StellarSdk.xdr.Uint64(deadline.toString()), - ), - StellarSdk.xdr.ScVal.scvBytes(metadataHash), - ); - - return [op]; - } catch (error) { - this.logger.error( - `Failed to create escrow initialization ops: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Creates operations for funding an escrow - */ - createFundingOps( - escrowId: string, - // amount: string, // Not used in contract call directly as it's part of escrow creation - // asset: StellarSdk.Asset, - ): StellarSdk.xdr.Operation[] { - try { - this.logger.log(`Creating funding ops for escrow ID: ${escrowId}`); - - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'deposit_funds', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - ); - - return [op]; - } catch (error) { - this.logger.error( - `Failed to create funding ops: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Creates operations for releasing a milestone payment - */ - createMilestoneReleaseOps( - escrowId: string, - milestoneId: number, - // releaserPublicKey: string, - // recipientPublicKey: string, - // amount: string, - // asset: StellarSdk.Asset, - ): StellarSdk.xdr.Operation[] { - try { - this.logger.log( - `Creating milestone release ops for escrow ID: ${escrowId}, milestone: ${milestoneId}`, - ); - - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'release_milestone', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - StellarSdk.xdr.ScVal.scvU32(milestoneId), - ); - - return [op]; - } catch (error) { - this.logger.error( - `Failed to create milestone release ops: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Creates operations for confirming delivery/acceptance - */ - createConfirmationOps( - escrowId: string, - confirmerPublicKey: string, - milestoneId: number, - ): StellarSdk.xdr.Operation[] { - try { - this.logger.log( - `Creating confirmation ops for escrow ID: ${escrowId}, milestone: ${milestoneId}`, - ); - - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'confirm_delivery', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - StellarSdk.xdr.ScVal.scvU32(milestoneId), - new StellarSdk.Address(confirmerPublicKey).toScVal(), - ); - - return [op]; - } catch (error) { - this.logger.error( - `Failed to create confirmation ops: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Creates operations for canceling an escrow - */ - createCancelOps( - escrowId: string, - // cancellerPublicKey: string, - ): StellarSdk.xdr.Operation[] { - try { - this.logger.log(`Creating cancel ops for escrow ID: ${escrowId}`); - - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'cancel_escrow', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - ); - - return [op]; - } catch (error) { - this.logger.error( - `Failed to create cancel ops: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Creates operations for completing an escrow - */ - createCompletionOps( - escrowId: string, - // completerPublicKey: string, - ): StellarSdk.xdr.Operation[] { - try { - this.logger.log(`Creating completion ops for escrow ID: ${escrowId}`); - - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'complete_escrow', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - ); - - return [op]; - } catch (error) { - this.logger.error( - `Failed to create completion ops: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Creates operations for raising a dispute - */ - createDisputeOps( - escrowId: string, - callerPublicKey: string, - ): StellarSdk.xdr.Operation[] { - try { - this.logger.log(`Creating dispute ops for escrow ID: ${escrowId}`); - - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'raise_dispute', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - new StellarSdk.Address(callerPublicKey).toScVal(), - ); - - return [op]; - } catch (error) { - this.logger.error( - `Failed to create dispute ops: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Creates operations for resolving a dispute - */ - createResolveDisputeOps( - escrowId: string, - winnerPublicKey: string, - splitWinnerAmount?: string, - ): StellarSdk.xdr.Operation[] { - try { - this.logger.log( - `Creating resolve dispute ops for escrow ID: ${escrowId}`, - ); - - const contract = new StellarSdk.Contract(this.contractId); - const op = contract.call( - 'resolve_dispute', - StellarSdk.xdr.ScVal.scvU64(new StellarSdk.xdr.Uint64(escrowId)), - new StellarSdk.Address(winnerPublicKey).toScVal(), - splitWinnerAmount - ? StellarSdk.xdr.ScVal.scvVec([ - StellarSdk.xdr.ScVal.scvI128( - new StellarSdk.xdr.Int128Parts({ - lo: new StellarSdk.xdr.Uint64(splitWinnerAmount), - hi: new StellarSdk.xdr.Int64('0'), - }), - ), - ]) - : StellarSdk.xdr.ScVal.scvVec([]), // Option::None - ); - - return [op]; - } catch (error) { - this.logger.error( - `Failed to create resolve dispute ops: ${this.getErrorMessage(error)}`, - ); - throw error; - } - } - - /** - * Safely extracts error message from unknown error type - */ - private getErrorMessage(error: unknown): string { - if (error instanceof Error) { - return error.message; - } - if (typeof error === 'object' && error !== null && 'message' in error) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - return String((error as any).message); - } - return 'Unknown error'; - } -} diff --git a/apps/backend/src/services/stellar/soroban-client.service.ts b/apps/backend/src/services/stellar/soroban-client.service.ts deleted file mode 100644 index b20525d5..00000000 --- a/apps/backend/src/services/stellar/soroban-client.service.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { Injectable, Logger, Inject } from '@nestjs/common'; -import { ConfigType } from '@nestjs/config'; -import * as StellarSdk from '@stellar/stellar-sdk'; -import stellarConfig from '../../config/stellar.config'; - -export interface OnchainEscrow { - status: string; - amount: string; - depositor: string; - recipient: string; -} - -@Injectable() -export class SorobanClientService { - private readonly logger = new Logger(SorobanClientService.name); - private rpcServer: StellarSdk.rpc.Server; - private networkPassphrase: string; - private contractId: string; - - constructor( - @Inject(stellarConfig.KEY) - private config: ConfigType, - ) { - const rpcUrl = - process.env.STELLAR_RPC_URL || 'https://soroban-testnet.stellar.org'; - this.rpcServer = new StellarSdk.rpc.Server(rpcUrl); - this.networkPassphrase = this.config.networkPassphrase; - this.contractId = process.env.STELLAR_CONTRACT_ID || ''; - - this.logger.log(`Initialized Soroban client with RPC: ${rpcUrl}`); - } - - /** - * Fetches the current state of an escrow from the contract storage - */ - async getEscrow(escrowId: number): Promise { - try { - this.logger.debug(`Fetching escrow ${escrowId} from contract`); - - const contract = new StellarSdk.Contract(this.contractId); - - // Get storage key for escrow: (Symbol("escrow"), u64) - const key = StellarSdk.xdr.ScVal.scvVec([ - StellarSdk.xdr.ScVal.scvSymbol('escrow'), - StellarSdk.xdr.ScVal.scvU64( - new StellarSdk.xdr.Uint64(escrowId.toString()), - ), - ]); - - const ledgerKey = StellarSdk.xdr.LedgerKey.contractData( - new StellarSdk.xdr.LedgerKeyContractData({ - contract: contract.address().toScAddress(), - key, - durability: StellarSdk.xdr.ContractDataDurability.persistent(), - }), - ); - - const response = await this.rpcServer.getLedgerEntries(ledgerKey); - - if (!response.entries || response.entries.length === 0) { - return null; - } - - const entry = response.entries[0]; - const scVal = StellarSdk.xdr.ScVal.fromXDR(entry.val.toXDR()); - - // Basic decoding (simplified for this task) - // In a real app, use a proper ScVal decoder or the contract-client - return this.decodeEscrowScVal(scVal); - } catch (error: unknown) { - if (error instanceof Error) { - this.logger.error(`Error fetching escrow: ${error.message}`); - } - return null; - } - } - - /** - * Decodes an ScVal representing the Escrow struct - */ - private decodeEscrowScVal(scVal: StellarSdk.xdr.ScVal): OnchainEscrow | null { - if (scVal.switch() !== StellarSdk.xdr.ScValType.scvMap()) { - return null; - } - - const map = scVal.map(); - if (!map) { - return null; - } - - const result: Partial = {}; - - map.forEach((entry) => { - const key = entry.key().sym().toString(); - const val = entry.val(); - - // Simplified decoding for status and amount - if (key === 'status') { - result.status = this.decodeStatus(val); - } else if (key === 'total_amount') { - result.amount = val.i128().lo().toString(); // Simplified i128 handle - } else if (key === 'depositor') { - result.depositor = StellarSdk.Address.fromScAddress( - val.address(), - ).toString(); - } else if (key === 'recipient') { - result.recipient = StellarSdk.Address.fromScAddress( - val.address(), - ).toString(); - } - }); - - return result as OnchainEscrow; - } - - private decodeStatus(val: StellarSdk.xdr.ScVal): string { - // EscrowStatus enum decoding - if (val.switch() === StellarSdk.xdr.ScValType.scvVec()) { - const vec = val.vec(); - if (vec && vec.length > 0) { - return vec[0].sym().toString(); - } - } - // If it's just a symbol (some SDK versions/encodings) - if (val.switch() === StellarSdk.xdr.ScValType.scvSymbol()) { - return val.sym().toString(); - } - return 'Unknown'; - } - - getContractId(): string { - return this.contractId; - } - - getRpc(): StellarSdk.rpc.Server { - return this.rpcServer; - } - - /** - * Decodes contract-specific error codes from Soroban XDR - */ - decodeContractError(errorCode: number): string { - const errorMap: Record = { - 1: 'EscrowNotFound', - 2: 'EscrowAlreadyExists', - 3: 'AlreadyFunded', - 4: 'NotFunded', - 5: 'InvalidMilestone', - 6: 'MilestoneAlreadyReleased', - 7: 'MilestoneNotReady', - 8: 'NotDepositor', - 9: 'NotArbiter', - 10: 'UnauthorizedAccess', - 11: 'InsufficientFunds', - 12: 'DeadlinePassed', - 13: 'DeadlineNotPassed', - 14: 'EscrowCompleted', - 15: 'EscrowCancelled', - 16: 'MilestoneNotReleased', - 17: 'EscrowNotActive', - 18: 'InvalidAmount', - }; - - return errorMap[errorCode] || `UnknownError(${errorCode})`; - } -} diff --git a/apps/backend/src/services/stellar/soroban-integration.spec.ts b/apps/backend/src/services/stellar/soroban-integration.spec.ts deleted file mode 100644 index af9f2a76..00000000 --- a/apps/backend/src/services/stellar/soroban-integration.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { EscrowOperationsService } from './escrow-operations'; -import * as StellarSdk from '@stellar/stellar-sdk'; - -describe('EscrowOperationsService Integration', () => { - let service: EscrowOperationsService; - - beforeEach(async () => { - // Mock valid contract ID (56 characters starting with C) - const validContractId = StellarSdk.StrKey.encodeContract(Buffer.alloc(32)); - process.env.STELLAR_CONTRACT_ID = validContractId; - - const module: TestingModule = await Test.createTestingModule({ - providers: [EscrowOperationsService], - }).compile(); - - service = module.get(EscrowOperationsService); - }); - - it('should create escrow initialization operations with correct Soroban call', () => { - const escrowId = '123'; - const depositor = StellarSdk.Keypair.random().publicKey(); - const recipient = StellarSdk.Keypair.random().publicKey(); - const token = StellarSdk.Keypair.random().publicKey(); // Or contract ID - const milestones = [ - { id: 1, amount: '1000', description: 'Test milestone' }, - ]; - const deadline = Math.floor(Date.now() / 1000) + 3600; - - const ops = service.createEscrowInitializationOps( - escrowId, - depositor, - recipient, - token, - milestones, - deadline, - '1'.repeat(64), // Valid non-zero hex metadata hash - ); - - const op = ops[0] as any as { - body: { - value: () => { - call: () => { functionName: () => { args: () => any[] } }; - }; - }; - }; - // In newer SDKs, op might be an XDR operation object - // or a higher level object with a different structure - expect(op).toBeDefined(); - }); - - it('should create funding operations', () => { - const ops = service.createFundingOps('123'); - expect(ops.length).toBe(1); - expect(ops[0]).toBeDefined(); - }); - - it('should create milestone release operations', () => { - const ops = service.createMilestoneReleaseOps('123', 1); - expect(ops.length).toBe(1); - expect(ops[0]).toBeDefined(); - }); - - it('should create dispute operations', () => { - const depositor = StellarSdk.Keypair.random().publicKey(); - const ops = service.createDisputeOps('123', depositor); - expect(ops.length).toBe(1); - expect(ops[0]).toBeDefined(); - }); -}); diff --git a/apps/backend/src/services/webhook/webhook.service.spec.ts b/apps/backend/src/services/webhook/webhook.service.spec.ts deleted file mode 100644 index ade8e3a5..00000000 --- a/apps/backend/src/services/webhook/webhook.service.spec.ts +++ /dev/null @@ -1,457 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { WebhookService } from './webhook.service'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { Webhook } from '../../modules/webhook/webhook.entity'; -import { WebhookDelivery } from '../../modules/webhook/entities/webhook-delivery.entity'; -import { Repository } from 'typeorm'; -import axios from 'axios'; -import { WebhookDeliveryStatus } from '../../types/webhook/webhook.types'; -import { - NotFoundException, - ForbiddenException, - UnprocessableEntityException, -} from '@nestjs/common'; - -jest.mock('axios'); -const mockedAxios = axios as jest.Mocked; - -describe('WebhookService', () => { - let service: WebhookService; - let webhookRepo: jest.Mocked>; - let deliveryRepo: jest.Mocked>; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - WebhookService, - { - provide: getRepositoryToken(Webhook), - useValue: { - find: jest.fn(), - findOne: jest.fn(), - create: jest.fn(), - save: jest.fn(), - delete: jest.fn(), - }, - }, - { - provide: getRepositoryToken(WebhookDelivery), - useValue: { - find: jest.fn(), - findOne: jest.fn(), - create: jest.fn(), - save: jest.fn(), - delete: jest.fn(), - findAndCount: jest.fn(), - count: jest.fn(), - }, - }, - ], - }).compile(); - - service = module.get(WebhookService); - webhookRepo = module.get(getRepositoryToken(Webhook)); - deliveryRepo = module.get(getRepositoryToken(WebhookDelivery)); - }); - - const mockWebhook = { - id: 'w1', - user: { id: 'u1' }, - url: 'http://test.com', - secret: 'test-secret', - events: ['escrow.created'], - isActive: true, - }; - - describe('createWebhook', () => { - it('should create a webhook if within limits', async () => { - webhookRepo.find.mockResolvedValue([]); - webhookRepo.create.mockReturnValue(mockWebhook as any); - webhookRepo.save.mockResolvedValue(mockWebhook as any); - - const result = await service.createWebhook('u1', 'test.com', 'secret', [ - 'escrow.created', - ]); - - expect(webhookRepo.save).toHaveBeenCalled(); - expect(result).toEqual(mockWebhook); - }); - - it('should throw if too many events', async () => { - await expect( - service.createWebhook( - 'u1', - 'test.com', - 'secret', - Array(10).fill('escrow.created') as any, - ), - ).rejects.toThrow(UnprocessableEntityException); - }); - - it('should throw if user exceeds webhook limit', async () => { - webhookRepo.find.mockResolvedValue(Array(11).fill(mockWebhook) as any); - await expect( - service.createWebhook('u1', 'test.com', 'secret', ['escrow.created']), - ).rejects.toThrow(UnprocessableEntityException); - }); - }); - - describe('deleteWebhook', () => { - it('should delete if owned by user', async () => { - webhookRepo.findOne.mockResolvedValue(mockWebhook as any); - await service.deleteWebhook('u1', 'w1'); - expect(webhookRepo.delete).toHaveBeenCalledWith('w1'); - }); - - it('should throw if not owned by user', async () => { - webhookRepo.findOne.mockResolvedValue({ - id: 'w1', - user: { id: 'u2' }, - } as any); - await expect(service.deleteWebhook('u1', 'w1')).rejects.toThrow( - ForbiddenException, - ); - }); - - it('should throw if webhook not found', async () => { - webhookRepo.findOne.mockResolvedValue(null); - await expect(service.deleteWebhook('u1', 'w1')).rejects.toThrow( - NotFoundException, - ); - }); - }); - - describe('dispatchEvent', () => { - it('should create a delivery record and attempt delivery for matching webhooks', async () => { - webhookRepo.find.mockResolvedValue([mockWebhook] as any); - const mockDelivery = { - id: 'd1', - webhookId: 'w1', - webhook: mockWebhook, - event: 'escrow.created', - payload: { - event: 'escrow.created', - data: { foo: 'bar' }, - timestamp: expect.any(String), - }, - status: WebhookDeliveryStatus.PENDING, - attempt: 1, - maxAttempts: 5, - nextRetryAt: expect.any(Date), - lastStatusCode: null, - lastError: null, - lastAttemptAt: null, - }; - deliveryRepo.create.mockReturnValue(mockDelivery as any); - deliveryRepo.save.mockResolvedValue(mockDelivery as any); - - const attemptSpy = jest - .spyOn(service, 'attemptDelivery') - .mockImplementation(() => Promise.resolve()); - - await service.dispatchEvent('escrow.created', { foo: 'bar' }); - - expect(deliveryRepo.create).toHaveBeenCalledWith( - expect.objectContaining({ - webhookId: 'w1', - event: 'escrow.created', - status: WebhookDeliveryStatus.PENDING, - attempt: 1, - maxAttempts: 5, - }), - ); - expect(deliveryRepo.save).toHaveBeenCalled(); - expect(attemptSpy).toHaveBeenCalledWith(mockDelivery); - }); - - it('should not create delivery for webhooks that do not match the event', async () => { - const nonMatchingWebhook = { ...mockWebhook, events: ['escrow.funded'] }; - webhookRepo.find.mockResolvedValue([nonMatchingWebhook] as any); - - await service.dispatchEvent('escrow.created', { foo: 'bar' }); - - expect(deliveryRepo.create).not.toHaveBeenCalled(); - }); - }); - - describe('attemptDelivery', () => { - const baseDelivery = { - id: 'd1', - webhookId: 'w1', - webhook: mockWebhook, - event: 'escrow.created', - payload: { event: 'escrow.created', data: {}, timestamp: 'now' }, - status: WebhookDeliveryStatus.PENDING, - attempt: 1, - maxAttempts: 5, - nextRetryAt: new Date(), - lastStatusCode: null, - lastError: null, - lastAttemptAt: null, - }; - - it('should mark as delivered on success', async () => { - mockedAxios.post.mockResolvedValue({ status: 200 }); - const delivery = { ...baseDelivery }; - deliveryRepo.save.mockImplementation((d) => Promise.resolve(d as any)); - - await service.attemptDelivery(delivery as any); - - expect(delivery.status).toBe(WebhookDeliveryStatus.DELIVERED); - expect(delivery.lastStatusCode).toBe(200); - expect(delivery.lastError).toBeNull(); - expect(delivery.nextRetryAt).toBeNull(); - expect(delivery.lastAttemptAt).toBeInstanceOf(Date); - }); - - it('should set status to retrying on failure with attempts remaining', async () => { - mockedAxios.post.mockRejectedValue(new Error('Network error')); - const delivery = { ...baseDelivery, attempt: 1 }; - deliveryRepo.save.mockImplementation((d) => Promise.resolve(d as any)); - - await service.attemptDelivery(delivery as any); - - expect(delivery.status).toBe(WebhookDeliveryStatus.RETRYING); - expect(delivery.attempt).toBe(2); - expect(delivery.lastError).toBe('Network error'); - expect(delivery.nextRetryAt).toBeInstanceOf(Date); - }); - - it('should calculate exponential backoff: 1s for attempt 1', async () => { - mockedAxios.post.mockRejectedValue(new Error('fail')); - const delivery = { ...baseDelivery, attempt: 1 }; - deliveryRepo.save.mockImplementation((d) => Promise.resolve(d as any)); - const before = Date.now(); - - await service.attemptDelivery(delivery as any); - - const backoffMs = delivery.nextRetryAt.getTime() - before; - expect(backoffMs).toBeGreaterThanOrEqual(900); - expect(backoffMs).toBeLessThanOrEqual(1200); - }); - - it('should calculate exponential backoff: 2s for attempt 2', async () => { - mockedAxios.post.mockRejectedValue(new Error('fail')); - const delivery = { ...baseDelivery, attempt: 2 }; - deliveryRepo.save.mockImplementation((d) => Promise.resolve(d as any)); - const before = Date.now(); - - await service.attemptDelivery(delivery as any); - - const backoffMs = delivery.nextRetryAt.getTime() - before; - expect(backoffMs).toBeGreaterThanOrEqual(1900); - expect(backoffMs).toBeLessThanOrEqual(2200); - }); - - it('should calculate exponential backoff: 4s for attempt 3', async () => { - mockedAxios.post.mockRejectedValue(new Error('fail')); - const delivery = { ...baseDelivery, attempt: 3 }; - deliveryRepo.save.mockImplementation((d) => Promise.resolve(d as any)); - const before = Date.now(); - - await service.attemptDelivery(delivery as any); - - const backoffMs = delivery.nextRetryAt.getTime() - before; - expect(backoffMs).toBeGreaterThanOrEqual(3900); - expect(backoffMs).toBeLessThanOrEqual(4200); - }); - - it('should mark as failed when max attempts exhausted', async () => { - mockedAxios.post.mockRejectedValue(new Error('Network error')); - const delivery = { ...baseDelivery, attempt: 5 }; - deliveryRepo.save.mockImplementation((d) => Promise.resolve(d as any)); - - await service.attemptDelivery(delivery as any); - - expect(delivery.status).toBe(WebhookDeliveryStatus.FAILED); - expect(delivery.nextRetryAt).toBeNull(); - expect(delivery.lastError).toBe('Network error'); - }); - - it('should capture HTTP status code from error response', async () => { - const axiosError = { - response: { status: 500 }, - message: 'Request failed with status code 500', - }; - mockedAxios.post.mockRejectedValue(axiosError); - const delivery = { ...baseDelivery, attempt: 1 }; - deliveryRepo.save.mockImplementation((d) => Promise.resolve(d as any)); - - await service.attemptDelivery(delivery as any); - - expect(delivery.lastStatusCode).toBe(500); - }); - - it('should mark as failed if webhook entity no longer exists', async () => { - const delivery = { ...baseDelivery, webhook: undefined, webhookId: 'w1' }; - webhookRepo.findOne.mockResolvedValue(null); - deliveryRepo.save.mockImplementation((d) => Promise.resolve(d as any)); - - await service.attemptDelivery(delivery as any); - - expect(delivery.status).toBe(WebhookDeliveryStatus.FAILED); - expect(delivery.lastError).toBe('Webhook not found'); - }); - }); - - describe('processRetries', () => { - it('should pick up retrying deliveries with past nextRetryAt', async () => { - const dueDeliveries = [ - { id: 'd1', status: WebhookDeliveryStatus.RETRYING }, - ]; - deliveryRepo.find.mockResolvedValue(dueDeliveries as any); - - const attemptSpy = jest - .spyOn(service, 'attemptDelivery') - .mockImplementation(() => Promise.resolve()); - - await service.processRetries(); - - expect(deliveryRepo.find).toHaveBeenCalledWith( - expect.objectContaining({ - where: { - status: WebhookDeliveryStatus.RETRYING, - nextRetryAt: expect.anything(), - }, - }), - ); - expect(attemptSpy).toHaveBeenCalledWith(dueDeliveries[0]); - }); - }); - - describe('processPending', () => { - it('should pick up pending deliveries with past nextRetryAt', async () => { - const pendingDeliveries = [ - { id: 'd2', status: WebhookDeliveryStatus.PENDING }, - ]; - deliveryRepo.find.mockResolvedValue(pendingDeliveries as any); - - const attemptSpy = jest - .spyOn(service, 'attemptDelivery') - .mockImplementation(() => Promise.resolve()); - - await service.processPending(); - - expect(deliveryRepo.find).toHaveBeenCalledWith( - expect.objectContaining({ - where: { - status: WebhookDeliveryStatus.PENDING, - nextRetryAt: expect.anything(), - }, - }), - ); - expect(attemptSpy).toHaveBeenCalledWith(pendingDeliveries[0]); - }); - }); - - describe('getFailedDeliveries', () => { - it('should return paginated failed deliveries', async () => { - const failedDeliveries = [ - { id: 'd1', status: WebhookDeliveryStatus.FAILED }, - ]; - deliveryRepo.findAndCount.mockResolvedValue([failedDeliveries as any, 1]); - - const result = await service.getFailedDelveys({ page: 1, limit: 20 }); - - expect(result.deliveries).toEqual(failedDeliveries); - expect(result.pagination).toEqual({ - page: 1, - limit: 20, - total: 1, - pages: 1, - }); - }); - }); - - describe('manualRetry', () => { - it('should reset a failed delivery and re-attempt', async () => { - const failedDelivery = { - id: 'd1', - status: WebhookDeliveryStatus.FAILED, - attempt: 5, - nextRetryAt: null, - lastStatusCode: 500, - lastError: 'Internal Server Error', - lastAttemptAt: new Date(), - webhook: mockWebhook, - }; - deliveryRepo.findOne.mockResolvedValue(failedDelivery as any); - deliveryRepo.save.mockImplementation((d) => Promise.resolve(d as any)); - const attemptSpy = jest - .spyOn(service, 'attemptDelivery') - .mockImplementation(() => Promise.resolve()); - - const result = await service.manualRetry('d1'); - - expect(result.status).toBe(WebhookDeliveryStatus.PENDING); - expect(result.attempt).toBe(1); - expect(result.lastStatusCode).toBeNull(); - expect(result.lastError).toBeNull(); - expect(attemptSpy).toHaveBeenCalled(); - }); - - it('should throw if delivery not found', async () => { - deliveryRepo.findOne.mockResolvedValue(null); - await expect(service.manualRetry('nonexistent')).rejects.toThrow( - NotFoundException, - ); - }); - - it('should throw if delivery is not in failed status', async () => { - deliveryRepo.findOne.mockResolvedValue({ - id: 'd1', - status: WebhookDeliveryStatus.DELIVERED, - } as any); - await expect(service.manualRetry('d1')).rejects.toThrow( - ForbiddenException, - ); - }); - }); - - describe('getHealthStats', () => { - it('should return correct health stats', async () => { - deliveryRepo.count - .mockResolvedValueOnce(100) - .mockResolvedValueOnce(80) - .mockResolvedValueOnce(10) - .mockResolvedValueOnce(5) - .mockResolvedValueOnce(5); - - const result = await service.getHealthStats(); - - expect(result).toEqual({ - total: 100, - delivered: 80, - failed: 10, - retrying: 5, - pending: 5, - successRate: 88.89, - failureRate: 11.11, - }); - }); - - it('should return zero rates when no completed deliveries exist', async () => { - deliveryRepo.count - .mockResolvedValueOnce(5) - .mockResolvedValueOnce(0) - .mockResolvedValueOnce(0) - .mockResolvedValueOnce(2) - .mockResolvedValueOnce(3); - - const result = await service.getHealthStats(); - - expect(result.successRate).toBe(0); - expect(result.failureRate).toBe(0); - }); - }); - - describe('verifySignature', () => { - it('should verify signature correctly', () => { - const payload: any = { foo: 'bar' }; - const secret = 'test-secret'; - const signature = service.signPayload(secret, payload); - const isValid = service.verifySignature(secret, payload, signature); - expect(isValid).toBe(true); - }); - }); -}); diff --git a/apps/backend/src/services/webhook/webhook.service.ts b/apps/backend/src/services/webhook/webhook.service.ts deleted file mode 100644 index 1c44c2d5..00000000 --- a/apps/backend/src/services/webhook/webhook.service.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { - Injectable, - NotFoundException, - ForbiddenException, - Logger, - UnprocessableEntityException, -} from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, LessThan } from 'typeorm'; -import { Cron, CronExpression } from '@nestjs/schedule'; -import { Webhook } from '../../modules/webhook/webhook.entity'; -import { WebhookDelivery } from '../../modules/webhook/entities/webhook-delivery.entity'; -import { - WebhookEvent, - WebhookPayload, - WebhookDeliveryStatus, -} from '../../types/webhook/webhook.types'; -import * as crypto from 'crypto'; -import axios from 'axios'; - -@Injectable() -export class WebhookService { - private readonly logger = new Logger(WebhookService.name); - private readonly MAX_WEBHOOKS_PER_USER = 10; - private readonly MAX_EVENTS_PER_WEBHOOK = 8; - private readonly MAX_RETRY_ATTEMPTS = 5; - private readonly BASE_RETRY_DELAY_MS = 1000; - - constructor( - @InjectRepository(Webhook) - private readonly webhookRepo: Repository, - @InjectRepository(WebhookDelivery) - private readonly deliveryRepo: Repository, - ) {} - - async createWebhook( - userId: string, - url: string, - secret: string, - events: WebhookEvent[], - ): Promise { - if (events.length > this.MAX_EVENTS_PER_WEBHOOK) { - throw new UnprocessableEntityException( - `Maximum ${this.MAX_EVENTS_PER_WEBHOOK} events allowed per webhook`, - ); - } - - const existingWebhooks = await this.getUserWebhooks(userId); - if (existingWebhooks.length >= this.MAX_WEBHOOKS_PER_USER) { - throw new UnprocessableEntityException( - `Maximum ${this.MAX_WEBHOOKS_PER_USER} webhooks allowed per user`, - ); - } - - const webhook = this.webhookRepo.create({ - url, - secret, - events, - user: { id: userId }, - isActive: true, - }); - return this.webhookRepo.save(webhook); - } - - async getUserWebhooks(userId: string): Promise { - return this.webhookRepo.find({ where: { user: { id: userId } } }); - } - - async deleteWebhook(userId: string, webhookId: string): Promise { - const webhook = await this.webhookRepo.findOne({ - where: { id: webhookId }, - relations: ['user'], - }); - if (!webhook) throw new NotFoundException('Webhook not found'); - if (webhook.user.id !== userId) - throw new ForbiddenException('Not your webhook'); - await this.webhookRepo.delete(webhookId); - } - - async dispatchEvent(event: WebhookEvent, data: unknown): Promise { - const webhooks = await this.webhookRepo.find({ where: { isActive: true } }); - const payload: WebhookPayload = { - event, - data, - timestamp: new Date().toISOString(), - }; - for (const webhook of webhooks) { - if (webhook.events.includes(event)) { - const delivery = this.deliveryRepo.create({ - webhook, - webhookId: webhook.id, - event, - payload: payload as unknown as Record, - status: WebhookDeliveryStatus.PENDING, - attempt: 1, - maxAttempts: this.MAX_RETRY_ATTEMPTS, - nextRetryAt: new Date(), - lastStatusCode: null, - lastError: null, - lastAttemptAt: null, - }); - const saved = await this.deliveryRepo.save(delivery); - void this.attemptDelivery(saved); - } - } - } - - async attemptDelivery(delivery: WebhookDelivery): Promise { - const webhook = - delivery.webhook ?? - (await this.webhookRepo.findOne({ where: { id: delivery.webhookId } })); - if (!webhook) { - delivery.status = WebhookDeliveryStatus.FAILED; - delivery.lastError = 'Webhook not found'; - delivery.lastAttemptAt = new Date(); - await this.deliveryRepo.save(delivery); - return; - } - - const payload = delivery.payload as unknown as WebhookPayload; - const signature = this.signPayload(webhook.secret, payload); - let statusCode: number | null = null; - let errorMsg: string | null = null; - - try { - const response = await axios.post(webhook.url, payload, { - headers: { - 'X-Vaultix-Signature': signature, - 'Content-Type': 'application/json', - }, - timeout: 5000, - }); - statusCode = response.status; - delivery.status = WebhookDeliveryStatus.DELIVERED; - delivery.nextRetryAt = null; - this.logger.log(`Webhook delivered to ${webhook.url}`); - } catch (err: unknown) { - if (typeof err === 'object' && err !== null && 'response' in err) { - const axiosErr = err as { - response?: { status?: number }; - message?: string; - }; - statusCode = axiosErr.response?.status ?? null; - errorMsg = axiosErr.message ?? 'Unknown error'; - } else if (typeof err === 'object' && err !== null && 'message' in err) { - errorMsg = (err as { message?: string }).message ?? 'Unknown error'; - } else { - errorMsg = 'Unknown error'; - } - - if (delivery.attempt < delivery.maxAttempts) { - delivery.status = WebhookDeliveryStatus.RETRYING; - const backoffMs = - this.BASE_RETRY_DELAY_MS * Math.pow(2, delivery.attempt - 1); - const nextRetry = new Date(Date.now() + backoffMs); - delivery.nextRetryAt = nextRetry; - delivery.attempt += 1; - this.logger.warn( - `Webhook delivery failed (attempt ${delivery.attempt - 1}) to ${webhook.url}: ${errorMsg}`, - ); - } else { - delivery.status = WebhookDeliveryStatus.FAILED; - delivery.nextRetryAt = null; - this.logger.error( - `Webhook delivery permanently failed to ${webhook.url}`, - ); - } - } - - delivery.lastStatusCode = statusCode; - delivery.lastError = errorMsg; - delivery.lastAttemptAt = new Date(); - await this.deliveryRepo.save(delivery); - } - - @Cron(CronExpression.EVERY_10_SECONDS) - async processRetries(): Promise { - const due = await this.deliveryRepo.find({ - where: { - status: WebhookDeliveryStatus.RETRYING, - nextRetryAt: LessThan(new Date()), - }, - relations: ['webhook'], - take: 50, - }); - - for (const delivery of due) { - void this.attemptDelivery(delivery); - } - } - - @Cron(CronExpression.EVERY_30_SECONDS) - async processPending(): Promise { - const pending = await this.deliveryRepo.find({ - where: { - status: WebhookDeliveryStatus.PENDING, - nextRetryAt: LessThan(new Date()), - }, - relations: ['webhook'], - take: 50, - }); - - for (const delivery of pending) { - void this.attemptDelivery(delivery); - } - } - - async getFailedDelveys(filters?: { - page?: number; - limit?: number; - webhookId?: string; - }): Promise<{ - deliveries: WebhookDelivery[]; - pagination: { page: number; limit: number; total: number; pages: number }; - }> { - const page = filters?.page ?? 1; - const limit = filters?.limit ?? 20; - - const where: Record = { - status: WebhookDeliveryStatus.FAILED, - }; - if (filters?.webhookId) where.webhookId = filters.webhookId; - - const [deliveries, total] = await this.deliveryRepo.findAndCount({ - where, - relations: ['webhook'], - skip: (page - 1) * limit, - take: limit, - order: { updatedAt: 'DESC' }, - }); - - return { - deliveries, - pagination: { - page, - limit, - total, - pages: Math.ceil(total / limit), - }, - }; - } - - async manualRetry(deliveryId: string): Promise { - const delivery = await this.deliveryRepo.findOne({ - where: { id: deliveryId }, - relations: ['webhook'], - }); - if (!delivery) throw new NotFoundException('Delivery not found'); - if (delivery.status !== WebhookDeliveryStatus.FAILED) { - throw new ForbiddenException('Only failed deliveries can be retried'); - } - - delivery.status = WebhookDeliveryStatus.PENDING; - delivery.attempt = 1; - delivery.nextRetryAt = new Date(); - delivery.lastStatusCode = null; - delivery.lastError = null; - delivery.lastAttemptAt = null; - await this.deliveryRepo.save(delivery); - - void this.attemptDelivery(delivery); - return delivery; - } - - async getHealthStats(): Promise<{ - total: number; - delivered: number; - failed: number; - retrying: number; - pending: number; - successRate: number; - failureRate: number; - }> { - const [total, delivered, failed, retrying, pending] = await Promise.all([ - this.deliveryRepo.count(), - this.deliveryRepo.count({ - where: { status: WebhookDeliveryStatus.DELIVERED }, - }), - this.deliveryRepo.count({ - where: { status: WebhookDeliveryStatus.FAILED }, - }), - this.deliveryRepo.count({ - where: { status: WebhookDeliveryStatus.RETRYING }, - }), - this.deliveryRepo.count({ - where: { status: WebhookDeliveryStatus.PENDING }, - }), - ]); - - const completed = delivered + failed; - return { - total, - delivered, - failed, - retrying, - pending, - successRate: - completed > 0 ? Math.round((delivered / completed) * 10000) / 100 : 0, - failureRate: - completed > 0 ? Math.round((failed / completed) * 10000) / 100 : 0, - }; - } - - signPayload(secret: string, payload: WebhookPayload): string { - const hmac = crypto.createHmac('sha256', secret); - hmac.update(JSON.stringify(payload)); - return hmac.digest('hex'); - } - - verifySignature( - secret: string, - payload: WebhookPayload, - signature: string, - ): boolean { - const expected = this.signPayload(secret, payload); - return crypto.timingSafeEqual( - Buffer.from(signature), - Buffer.from(expected), - ); - } -} diff --git a/apps/backend/src/types/stellar.types.ts b/apps/backend/src/types/stellar.types.ts deleted file mode 100644 index f9a9d3e9..00000000 --- a/apps/backend/src/types/stellar.types.ts +++ /dev/null @@ -1,78 +0,0 @@ -// Type definitions for Stellar SDK to avoid 'any' usage -import * as StellarSdk from '@stellar/stellar-sdk'; -export interface StellarAccountResponse extends StellarSdk.Account { - id: string; - account_id: string; - sequence: string; - balances: Array<{ - balance: string; - asset_type: string; - asset_code?: string; - asset_issuer?: string; - }>; - thresholds: { - low_threshold: number; - med_threshold: number; - high_threshold: number; - }; - flags: { - auth_required: boolean; - auth_revocable: boolean; - auth_immutable: boolean; - }; - signers: Array<{ - key: string; - weight: number; - type: string; - }>; - data: Record; -} - -export interface StellarSubmitTransactionResponse { - hash: string; - ledger: number; - envelope_xdr: string; - result_xdr: string; - result_meta_xdr: string; - paging_token: string; -} - -export interface StellarTransactionResponse { - id: string; - paging_token: string; - successful: boolean; - hash: string; - ledger: number; - created_at: string; - source_account: string; - source_account_sequence: string; - fee_charged: string; - max_fee: string; - operation_count: number; - envelope_xdr: string; - result_xdr: string; - result_meta_xdr: string; - fee_meta_xdr: string; - memo_type: string; - memo?: string; - signatures: string[]; - valid_after?: string; - valid_before?: string; -} - -export interface StellarHorizonError { - type: string; - title: string; - status: number; - detail: string; - extras?: { - envelope_xdr: string; - result_codes: { - transaction: string; - operations?: string[]; - }; - result_xdr: string; - }; -} - -export type StellarServer = StellarSdk.Horizon.Server; diff --git a/apps/backend/src/types/types.doc b/apps/backend/src/types/types.doc deleted file mode 100644 index ee6fe952..00000000 --- a/apps/backend/src/types/types.doc +++ /dev/null @@ -1 +0,0 @@ -this types is is used to mitigate "any" usage \ No newline at end of file diff --git a/apps/backend/src/types/webhook/webhook.types.ts b/apps/backend/src/types/webhook/webhook.types.ts deleted file mode 100644 index 01bb2e9f..00000000 --- a/apps/backend/src/types/webhook/webhook.types.ts +++ /dev/null @@ -1,24 +0,0 @@ -export enum WebhookDeliveryStatus { - PENDING = 'pending', - DELIVERED = 'delivered', - RETRYING = 'retrying', - FAILED = 'failed', -} - -export type WebhookEvent = - | 'escrow.created' - | 'escrow.funded' - | 'escrow.released' - | 'escrow.cancelled' - | 'escrow.expired' - | 'escrow.disputed' - | 'escrow.resolved' - | 'escrow.milestone_released' - | 'condition.fulfilled' - | 'condition.confirmed'; - -export interface WebhookPayload { - event: WebhookEvent; - data: unknown; - timestamp: string; -} diff --git a/apps/backend/src/utils/circuit-breaker.util.ts b/apps/backend/src/utils/circuit-breaker.util.ts new file mode 100644 index 00000000..ae2a8f81 --- /dev/null +++ b/apps/backend/src/utils/circuit-breaker.util.ts @@ -0,0 +1,59 @@ +export interface CircuitBreakerState { + isOpen: boolean; + failureCount: number; + lastFailureTime: number; + nextAttemptTime: number; +} + +export class CircuitBreaker { + private state: CircuitBreakerState = { + isOpen: false, + failureCount: 0, + lastFailureTime: 0, + nextAttemptTime: 0, + }; + + isOpen(config: CircuitBreakerConfig): boolean { + if (!this.state.isOpen) return false; + + const now = Date.now(); + if (now < this.state.nextAttemptTime) return true; + + this.reset(); + return false; + } + + recordFailure(config: CircuitBreakerConfig): void { + this.state.failureCount++; + this.state.lastFailureTime = Date.now(); + + if (this.state.failureCount >= config.maxFailures) { + this.state.isOpen = true; + this.state.nextAttemptTime = Date.now() + config.resetTimeout; + } + } + + recordSuccess(): void { + if (this.state.isOpen) { + this.reset(); + } + } + + reset(): void { + this.state = { + isOpen: false, + failureCount: 0, + lastFailureTime: 0, + nextAttemptTime: 0, + }; + } + + getState(): CircuitBreakerState { + return { ...this.state }; + } +} + +export interface CircuitBreakerConfig { + maxFailures: number; + resetTimeout: number; +} diff --git a/apps/backend/src/utils/logger.util.ts b/apps/backend/src/utils/logger.util.ts deleted file mode 100644 index abf96699..00000000 --- a/apps/backend/src/utils/logger.util.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Logger } from '@nestjs/common'; - -export interface LogContext { - correlationId?: string; - userId?: string; - action?: string; -} - -function generateId(): string { - return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`; -} - -export class CorrelationLogger { - private readonly logger: Logger; - - constructor(context: string) { - this.logger = new Logger(context); - } - - private format(message: string, ctx?: LogContext): string { - const cid = ctx?.correlationId ?? generateId(); - const meta = ctx ? { ...ctx, correlationId: cid } : { correlationId: cid }; - return `[cid:${cid}] ${message} | ${JSON.stringify(meta)}`; - } - - log(message: string, ctx?: LogContext): void { - this.logger.log(this.format(message, ctx)); - } - - warn(message: string, ctx?: LogContext): void { - this.logger.warn(this.format(message, ctx)); - } - - error(message: string, ctx?: LogContext): void { - this.logger.error(this.format(message, ctx)); - } -} - -export function createLogger(context: string): CorrelationLogger { - return new CorrelationLogger(context); -} diff --git a/apps/backend/src/utils/retry.util.ts b/apps/backend/src/utils/retry.util.ts deleted file mode 100644 index 35b5c4d7..00000000 --- a/apps/backend/src/utils/retry.util.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Exponential backoff retry utility - * @param fn The function to retry - * @param maxRetries Maximum number of retry attempts - * @param baseDelay Base delay in milliseconds - * @param factor Exponential factor (default 2) - * @returns Promise resolving to the result of the function - */ -export async function retryWithBackoff( - fn: () => Promise, - maxRetries: number, - baseDelay: number, - factor: number = 2, -): Promise { - let lastError: unknown; - - for (let attempt = 0; attempt <= maxRetries; attempt++) { - try { - return await fn(); - } catch (error) { - lastError = error; - - if (attempt === maxRetries) { - break; - } - - // Calculate delay with exponential backoff and jitter - const delay = baseDelay * Math.pow(factor, attempt); - const jitter = Math.random() * 0.1 * delay; // Add up to 10% jitter - const totalDelay = delay + jitter; - - await new Promise((resolve) => setTimeout(resolve, totalDelay)); - } - } - - throw lastError instanceof Error ? lastError : new Error(String(lastError)); -} diff --git a/apps/backend/src/utils/stellar-rpc-error-handler.util.ts b/apps/backend/src/utils/stellar-rpc-error-handler.util.ts new file mode 100644 index 00000000..0a19e814 --- /dev/null +++ b/apps/backend/src/utils/stellar-rpc-error-handler.util.ts @@ -0,0 +1,198 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { CircuitBreaker, CircuitBreakerConfig } from './circuit-breaker.util'; +import { + StellarRpcException, + StellarRpcTimeoutException, + StellarRpcTransactionException, + StellarRpcAccountException, + StellarRpcNetworkException, + StellarRpcValidationException +} from './stellar-rpc-exception'; + +@Injectable() +export class StellarRpcErrorHandler { + private readonly logger = new Logger(StellarRpcErrorHandler.name); + private readonly circuitBreaker: CircuitBreaker; + private readonly circuitBreakerConfig: CircuitBreakerConfig; + + constructor(private configService: ConfigService) { + this.circuitBreakerConfig = { + maxFailures: this.configService.get('STELLAR_CIRCUIT_BREAKER_MAX_FAILURES', 5), + resetTimeout: this.configService.get('STELLAR_CIRCUIT_BREAKER_RESET_TIMEOUT', 60000), + }; + this.circuitBreaker = new CircuitBreaker(); + } + + public canRetry(error: any): boolean { + if (this.circuitBreaker.isOpen(this.circuitBreakerConfig)) { + this.logger.warn(`Circuit breaker is open, refusing to retry. State: ${JSON.stringify(this.circuitBreaker.getState())}`); + return false; + } + + const status = this.getHttpStatusFromError(error); + + if (status && status >= 400 && status < 500) { + this.logger.debug(`Non-retryable error (HTTP ${status}): ${error.message || error}`); + return false; + } + + return this.isTransientError(error); + } + + public recordFailure(): void { + this.circuitBreaker.recordFailure(this.circuitBreakerConfig); + this.logger.warn(`Recorded failure. Circuit breaker state: ${JSON.stringify(this.circuitBreaker.getState())}`); + } + + public recordSuccess(): void { + this.circuitBreaker.recordSuccess(); + this.logger.debug(`Recorded success. Circuit breaker state: ${JSON.stringify(this.circuitBreaker.getState())}`); + } + + public getCircuitBreakerState(): any { + return this.circuitBreaker.getState(); + } + + public toStellarRpcException(error: any, defaultMessage: string): StellarRpcException { + const status = this.getHttpStatusFromError(error); + const message = this.extractErrorMessage(error); + + if (status && status === HttpStatus.GATEWAY_TIMEOUT) { + return new StellarRpcTimeoutException(`${defaultMessage}: ${message}`); + } + + if (this.isNetworkError(error)) { + return new StellarRpcNetworkException(`${defaultMessage}: ${message}`); + } + + if (status && status >= 400 && status < 500) { + return new StellarRpcValidationException(`${defaultMessage}: ${message}`); + } + + if (this.isAccountError(error)) { + return new StellarRpcAccountException(`${defaultMessage}: ${message}`); + } + + if (this.isTransactionError(error)) { + return new StellarRpcTransactionException(`${defaultMessage}: ${message}`); + } + + return new StellarRpcException( + `${defaultMessage}: ${message}`, + 'RPC_UNKNOWN_ERROR', + HttpStatus.BAD_GATEWAY, + 'network' + ); + } + + public logError(operation: string, error: any, context: string = ''): void { + const status = this.getHttpStatusFromError(error); + const message = this.extractErrorMessage(error); + + const contextLog = context ? ` (context: ${context})` : ''; + const statusLog = status ? ` (HTTP ${status})` : ''; + + this.logger.error( + `Stellar RPC error in ${operation}${contextLog}${statusLog}: ${message}` + ); + } + + private getHttpStatusFromError(error: any): number | undefined { + if (error && typeof error === 'object') { + if ('status' in error && typeof error.status === 'number') { + return error.status; + } + if (error.response && error.response.status) { + return error.response.status; + } + } + return undefined; + } + + private extractErrorMessage(error: any): string { + if (!error) return 'Unknown error'; + + if (error instanceof Error) { + return error.message; + } + + if (typeof error === 'object') { + if ('message' in error && typeof error.message === 'string') { + return error.message; + } + if ('detail' in error && typeof error.detail === 'string') { + return error.detail; + } + if ('title' in error && typeof error.title === 'string') { + return error.title; + } + } + + return String(error); + } + + private isTransientError(error: any): boolean { + if (!error) return true; + + if (this.isNetworkError(error)) return true; + if (this.isTimeoutError(error)) return true; + if (this.isStellarError(error) && !this.isClientError(error)) return true; + + return false; + } + + private isNetworkError(error: any): boolean { + if (!error) return false; + + const errorStr = String(error).toLowerCase(); + return errorStr.includes('network error') || + errorStr.includes('enotfound') || + errorStr.includes('etimedout') || + errorStr.includes('econnrefused'); + } + + private isTimeoutError(error: any): boolean { + if (!error) return false; + + const errorStr = String(error).toLowerCase(); + return errorStr.includes('timeout') || + errorStr.includes('timed out') || + errorStr.includes('gateway timeout'); + } + + private isStellarError(error: any): boolean { + if (!error) return false; + + const errorStr = String(error).toLowerCase(); + return errorStr.includes('stellar') || + (error.response && typeof error.response === 'object') || + (error.constructor && error.constructor.name === 'Error'); + } + + private isClientError(error: any): boolean { + const status = this.getHttpStatusFromError(error); + return status ? status >= 400 && status < 500 : false; + } + + private isAccountError(error: any): boolean { + if (!error) return false; + + if (this.isClientError(error)) { + const errorStr = String(error).toLowerCase(); + return errorStr.includes('account') || + errorStr.includes('not found') || + errorStr.includes('unauthorized'); + } + return false; + } + + private isTransactionError(error: any): boolean { + if (!error) return false; + + const errorStr = String(error).toLowerCase(); + return errorStr.includes('transaction') || + errorStr.includes('insufficient balance') || + errorStr.includes('bad transaction'); + } +} diff --git a/apps/backend/src/utils/token.ts b/apps/backend/src/utils/token.ts deleted file mode 100644 index 4881c350..00000000 --- a/apps/backend/src/utils/token.ts +++ /dev/null @@ -1,48 +0,0 @@ -const ACCESS_KEY = 'accessToken'; -const REFRESH_KEY = 'refreshToken'; - -// Browser localStorage type -interface StorageLike { - getItem(key: string): string | null; - setItem(key: string, value: string): void; - removeItem(key: string): void; -} - -const getLocalStorage = (): StorageLike | null => { - if (typeof globalThis.window !== 'undefined') { - return globalThis.window.localStorage; - } - return null; -}; - -export const setTokens = (access: string, refresh: string): void => { - const storage = getLocalStorage(); - if (storage) { - storage.setItem(ACCESS_KEY, access); - storage.setItem(REFRESH_KEY, refresh); - } -}; - -export const getAccessToken = (): string | null => { - const storage = getLocalStorage(); - if (storage) { - return storage.getItem(ACCESS_KEY); - } - return null; -}; - -export const getRefreshToken = (): string | null => { - const storage = getLocalStorage(); - if (storage) { - return storage.getItem(REFRESH_KEY); - } - return null; -}; - -export const clearTokens = (): void => { - const storage = getLocalStorage(); - if (storage) { - storage.removeItem(ACCESS_KEY); - storage.removeItem(REFRESH_KEY); - } -}; diff --git a/apps/backend/test/e2e/admin.e2e-spec.ts b/apps/backend/test/e2e/admin.e2e-spec.ts deleted file mode 100644 index d6101a16..00000000 --- a/apps/backend/test/e2e/admin.e2e-spec.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import request from 'supertest'; -import { AppModule } from '../../src/app.module'; -import { createTestApp } from '../setup/test-app.factory'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { User, UserRole } from 'src/modules/user/entities/user.entity'; -import { AuthGuard } from '../../src/modules/auth/middleware/auth.guard'; -import { Repository } from 'typeorm'; - -describe('Admin API (e2e)', () => { - let app: INestApplication; - let adminToken: string; - let userToken: string; - let targetUserId: string; - - beforeAll(async () => { - app = await createTestApp((builder) => - builder.overrideGuard(AuthGuard).useValue({ - canActivate: (context: import('@nestjs/common').ExecutionContext) => { - const req = context.switchToHttp().getRequest<{ - headers: { authorization?: string }; - user?: unknown; - }>(); - if (req.headers.authorization === 'Bearer admin-jwt-token') { - req.user = { userId: 'admin-id', role: UserRole.ADMIN }; - return true; - } - if (req.headers.authorization === 'Bearer user-jwt-token') { - req.user = { userId: 'user-id', role: UserRole.USER }; - return true; - } - return false; - }, - }), - ); - - // Create test users - const userRepository = app.get>(getRepositoryToken(User)); - - const admin = userRepository.create({ - walletAddress: 'ADMIN_TEST_WALLET', - role: UserRole.ADMIN, - isActive: true, - }); - await userRepository.save(admin); - - const regularUser = userRepository.create({ - walletAddress: 'USER_TEST_WALLET', - role: UserRole.USER, - isActive: true, - }); - const savedRegularUser = await userRepository.save(regularUser); - targetUserId = savedRegularUser.id; - - // Get tokens (simplified for testing) - adminToken = 'admin-jwt-token'; - userToken = 'user-jwt-token'; - }); - - afterAll(async () => { - if (app) { - await app.close(); - } - }); - - describe('/admin/escrows (GET)', () => { - it('should return all escrows for admin', () => { - const server = app.getHttpServer() as unknown as import('http').Server; - return request(server) - .get('/admin/escrows') - .set('Authorization', `Bearer ${adminToken}`) - .expect(200); - }); - - it('should forbid access for regular users', () => { - const server = app.getHttpServer() as unknown as import('http').Server; - return request(server) - .get('/admin/escrows') - .set('Authorization', `Bearer ${userToken}`) - .expect(403); - }); - }); - - describe('/admin/users (GET)', () => { - it('should return all users for admin', () => { - const server = app.getHttpServer() as unknown as import('http').Server; - return request(server) - .get('/admin/users') - .set('Authorization', `Bearer ${adminToken}`) - .expect(200); - }); - - it('should forbid access for regular users', () => { - const server = app.getHttpServer() as unknown as import('http').Server; - return request(server) - .get('/admin/users') - .set('Authorization', `Bearer ${userToken}`) - .expect(403); - }); - }); - - describe('/admin/stats (GET)', () => { - it('should return platform statistics for admin', () => { - const server = app.getHttpServer() as unknown as import('http').Server; - return request(server) - .get('/admin/stats') - .set('Authorization', `Bearer ${adminToken}`) - .expect(200); - }); - - it('should forbid access for regular users', () => { - const server = app.getHttpServer() as unknown as import('http').Server; - return request(server) - .get('/admin/stats') - .set('Authorization', `Bearer ${userToken}`) - .expect(403); - }); - }); - - describe('/admin/users/:id/suspend (POST)', () => { - it('should suspend user for admin', () => { - const server = app.getHttpServer() as unknown as import('http').Server; - return request(server) - .post(`/admin/users/${targetUserId}/suspend`) - .set('Authorization', `Bearer ${adminToken}`) - .expect(200); - }); - - it('should forbid access for regular users', () => { - const server = app.getHttpServer() as unknown as import('http').Server; - return request(server) - .post(`/admin/users/${targetUserId}/suspend`) - .set('Authorization', `Bearer ${userToken}`) - .expect(403); - }); - }); -}); diff --git a/apps/backend/test/e2e/app.e2e-spec.ts b/apps/backend/test/e2e/app.e2e-spec.ts deleted file mode 100644 index 34583aaf..00000000 --- a/apps/backend/test/e2e/app.e2e-spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import request from 'supertest'; -import { AppModule } from '../../src/app.module'; -import { createTestApp } from '../setup/test-app.factory'; - -describe('AppController (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - app = await createTestApp(); - }); - - it('/ (GET)', () => { - const server = app.getHttpServer() as unknown as import('http').Server; - return request(server).get('/').expect(200).expect('Hello World!'); - }); - - afterAll(async () => { - if (app) { - await app.close(); - } - }); -}); diff --git a/apps/backend/test/e2e/auth.e2e-spec.ts b/apps/backend/test/e2e/auth.e2e-spec.ts deleted file mode 100644 index 08889e9e..00000000 --- a/apps/backend/test/e2e/auth.e2e-spec.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication, ValidationPipe } from '@nestjs/common'; -import request from 'supertest'; -import type { Server } from 'http'; -import { AppModule } from '../../src/app.module'; -import { createTestApp } from '../setup/test-app.factory'; -import { Keypair } from 'stellar-sdk'; - -// Mock Stellar keypair for testing -// No mock needed, using real Keypair - -function createMockKeypair(): Keypair { - return Keypair.random(); -} - -interface ChallengeResponse { - nonce: string; - message: string; -} - -interface TokenResponse { - accessToken: string; - refreshToken: string; -} - -interface UserResponse { - id: string; - walletAddress: string; - isActive: boolean; - createdAt: string; -} - -describe('AuthController (e2e)', () => { - let app: INestApplication; - let httpServer: Server; - let testKeypair: Keypair; - let testWalletAddress: string; - let accessToken: string; - beforeAll(async () => { - app = await createTestApp(undefined, (appInstance) => { - appInstance.useGlobalPipes( - new ValidationPipe({ - whitelist: true, - forbidNonWhitelisted: true, - transform: true, - }), - ); - }); - httpServer = app.getHttpServer() as Server; - - // Generate a random keypair for testing - testKeypair = createMockKeypair(); - testWalletAddress = testKeypair.publicKey(); - }); - - afterAll(async () => { - if (app) { - await app.close(); - } - }); - - describe('/auth/challenge (POST)', () => { - it('should return a unique nonce for a wallet address', async () => { - const response = await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: testWalletAddress }) - .expect(200); - - const body = response.body as ChallengeResponse; - expect(body).toHaveProperty('nonce'); - expect(body).toHaveProperty('message'); - expect(body.message).toContain(body.nonce); - expect(body.nonce).toMatch(/^[a-f0-9]{32}$/); - }); - - it('should return 400 for invalid wallet address', async () => { - await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: 'invalid-address' }) - .expect(400); - }); - }); - - describe('/auth/verify (POST)', () => { - it('should verify a valid signature and return tokens', async () => { - // First get a challenge - const challengeResponse = await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: testWalletAddress }) - .expect(200); - - const message = (challengeResponse.body as ChallengeResponse).message; - const signature = testKeypair.sign(Buffer.from(message)).toString('hex'); - - const response = await request(httpServer) - .post('/auth/verify') - .send({ - signature: signature, - publicKey: testWalletAddress, - }) - .expect(200); - - const body = response.body as TokenResponse; - expect(body).toHaveProperty('accessToken'); - expect(body).toHaveProperty('refreshToken'); - expect(body.accessToken).toBeDefined(); - expect(body.refreshToken).toBeDefined(); - }); - - it('should return 401 for invalid signature', async () => { - await request(httpServer) - .post('/auth/verify') - .send({ - signature: 'invalid-signature', - publicKey: testWalletAddress, - }) - .expect(401); - }); - }); - - describe('/auth/me (GET)', () => { - beforeEach(async () => { - // Get a valid access token - const challengeResponse = await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: testWalletAddress }) - .expect(200); - - const message = (challengeResponse.body as ChallengeResponse).message; - const signature = testKeypair.sign(Buffer.from(message)).toString('hex'); - - const verifyResponse = await request(httpServer) - .post('/auth/verify') - .send({ - signature: signature, - publicKey: testWalletAddress, - }) - .expect(200); - - accessToken = (verifyResponse.body as TokenResponse).accessToken; - }); - - it('should return current user with valid token', async () => { - const response = await request(httpServer) - .get('/auth/me') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as UserResponse; - expect(body).toHaveProperty('id'); - expect(body).toHaveProperty('walletAddress'); - expect(body).toHaveProperty('isActive'); - expect(body).toHaveProperty('createdAt'); - expect(body.walletAddress).toBe(testWalletAddress); - expect(body.isActive).toBe(true); - }); - - it('should return 401 without token', async () => { - await request(httpServer).get('/auth/me').expect(401); - }); - - it('should return 401 with invalid token', async () => { - await request(httpServer) - .get('/auth/me') - .set('Authorization', 'Bearer invalid-token') - .expect(401); - }); - }); -}); diff --git a/apps/backend/test/e2e/escrow.e2e-spec.ts b/apps/backend/test/e2e/escrow.e2e-spec.ts deleted file mode 100644 index a87e8f52..00000000 --- a/apps/backend/test/e2e/escrow.e2e-spec.ts +++ /dev/null @@ -1,977 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication, ValidationPipe } from '@nestjs/common'; -import request from 'supertest'; -import type { Server } from 'http'; -import { DataSource, Repository } from 'typeorm'; -import { getRepositoryToken } from '@nestjs/typeorm'; -import { AppModule } from '../../src/app.module'; -import { createTestApp } from '../setup/test-app.factory'; -import { Keypair } from 'stellar-sdk'; -import { - Escrow, - EscrowStatus, - EscrowType, -} from '../../src/modules/escrow/entities/escrow.entity'; -import { PartyRole } from '../../src/modules/escrow/entities/party.entity'; -import { AllowedAsset } from '../../src/modules/assets/entities/allowed-asset.entity'; - -// No mock needed, using real Keypair - -function createMockKeypair(): Keypair { - return Keypair.random(); -} - -describe('Escrow (e2e)', () => { - let app: INestApplication; - let httpServer: Server; - let testKeypair: Keypair; - let testWalletAddress: string; - let accessToken: string; - let userId: string; - - let secondKeypair: Keypair; - let secondWalletAddress: string; - let secondAccessToken: string; - let secondUserId: string; - let escrowRepository: Repository; - - let arbitratorKeypair: Keypair; - let arbitratorWalletAddress: string; - let arbitratorAccessToken: string; - let arbitratorUserId: string; - - beforeAll(async () => { - // Use an isolated in-memory SQLite database for every test run - process.env.DATABASE_PATH = ':memory:'; - process.env.NODE_ENV = 'test'; - - testKeypair = createMockKeypair(); - testWalletAddress = testKeypair.publicKey(); - - secondKeypair = createMockKeypair(); - secondWalletAddress = secondKeypair.publicKey(); - - app = await createTestApp(); - httpServer = app.getHttpServer() as Server; - escrowRepository = app.get(DataSource).getRepository(Escrow); - - const allowedAssetRepository = app - .get(DataSource) - .getRepository(AllowedAsset); - await allowedAssetRepository.save({ - code: 'XLM', - displayName: 'Stellar Lumens', - decimals: 7, - active: true, - }); - - // Authenticate first user - const challengeResponse = await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: testWalletAddress }); - - const message = (challengeResponse.body as { message: string }).message; - const signature = testKeypair.sign(Buffer.from(message)).toString('hex'); - - const verifyResponse = await request(httpServer).post('/auth/verify').send({ - walletAddress: testWalletAddress, - signature: signature, - publicKey: testWalletAddress, - }); - - accessToken = (verifyResponse.body as { accessToken: string }).accessToken; - - const meResponse = await request(httpServer) - .get('/auth/me') - .set('Authorization', `Bearer ${accessToken}`); - userId = (meResponse.body as { id: string }).id; - - // Authenticate second user - const challenge2 = await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: secondWalletAddress }); - - const message2 = (challenge2.body as { message: string }).message; - const signature2 = secondKeypair - .sign(Buffer.from(message2)) - .toString('hex'); - - const verify2 = await request(httpServer).post('/auth/verify').send({ - walletAddress: secondWalletAddress, - signature: signature2, - publicKey: secondWalletAddress, - }); - - secondAccessToken = (verify2.body as { accessToken: string }).accessToken; - - const me2 = await request(httpServer) - .get('/auth/me') - .set('Authorization', `Bearer ${secondAccessToken}`); - secondUserId = (me2.body as { id: string }).id; - - // Authenticate arbitrator user - arbitratorKeypair = createMockKeypair(); - arbitratorWalletAddress = arbitratorKeypair.publicKey(); - - const challenge3 = await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: arbitratorWalletAddress }); - - const message3 = (challenge3.body as { message: string }).message; - const signature3 = arbitratorKeypair - .sign(Buffer.from(message3)) - .toString('hex'); - - const verify3 = await request(httpServer).post('/auth/verify').send({ - walletAddress: arbitratorWalletAddress, - signature: signature3, - publicKey: arbitratorWalletAddress, - }); - - arbitratorAccessToken = (verify3.body as { accessToken: string }) - .accessToken; - - const me3 = await request(httpServer) - .get('/auth/me') - .set('Authorization', `Bearer ${arbitratorAccessToken}`); - arbitratorUserId = (me3.body as { id: string }).id; - - // Grab the Escrow repository for direct DB manipulation in dispute tests - escrowRepository = app.get>(getRepositoryToken(Escrow)); - }); - - afterAll(async () => { - if (app) { - await app.close(); - } - }); - - interface EscrowResponse { - id: string; - title: string; - amount: number; - status: string; - creatorId: string; - conditions?: { description: string; type: string }[]; - } - - interface EscrowListResponse { - data: EscrowResponse[]; - total: number; - page: number; - limit: number; - } - - interface EscrowOverviewItem { - escrowId: string; - depositor: string; - recipient: string | null; - token: string; - totalAmount: number; - totalReleased: number; - remainingAmount: number; - status: string; - deadline: string | null; - createdAt: string; - updatedAt: string; - } - - interface EscrowOverviewResponse { - data: EscrowOverviewItem[]; - totalItems: number; - totalPages: number; - page: number; - pageSize: number; - } - - describe('POST /escrows', () => { - it('should create an escrow', async () => { - const response = await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: 'Test Escrow', - description: 'Test description', - amount: 100, - asset: { code: 'XLM' }, - parties: [{ userId: secondUserId, role: PartyRole.SELLER }], - }) - .expect(201); - - const body = response.body as EscrowResponse; - expect(body).toHaveProperty('id'); - expect(body.title).toBe('Test Escrow'); - expect(body.amount).toBe(100); - expect(body.status).toBe('pending'); - expect(body.creatorId).toBe(userId); - }); - - it('should create an escrow with conditions', async () => { - const response = await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: 'Escrow with Conditions', - amount: 200, - parties: [{ userId: secondUserId, role: PartyRole.SELLER }], - conditions: [ - { description: 'Goods delivered', type: 'manual' }, - { description: 'Inspection passed', type: 'manual' }, - ], - }) - .expect(201); - - const body = response.body as EscrowResponse; - expect(body.conditions).toHaveLength(2); - }); - - it('should return 400 for invalid data', async () => { - await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: 'Test', - // missing required fields - }) - .expect(400); - }); - - it('should return 401 without auth token', async () => { - await request(httpServer) - .post('/escrows') - .send({ - title: 'Test Escrow', - amount: 100, - parties: [{ userId: secondUserId, role: PartyRole.SELLER }], - }) - .expect(401); - }); - }); - - describe('GET /escrows', () => { - it('should return user escrows', async () => { - const response = await request(httpServer) - .get('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EscrowListResponse; - expect(body).toHaveProperty('data'); - expect(body).toHaveProperty('total'); - expect(body).toHaveProperty('page'); - expect(body).toHaveProperty('limit'); - expect(Array.isArray(body.data)).toBe(true); - }); - - it('should support pagination', async () => { - const response = await request(httpServer) - .get('/escrows?page=1&limit=5') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EscrowListResponse; - expect(body.page).toBe(1); - expect(body.limit).toBe(5); - }); - - it('should filter by status', async () => { - const response = await request(httpServer) - .get('/escrows?status=pending') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EscrowListResponse; - body.data.forEach((escrow: EscrowResponse) => { - expect(escrow.status).toBe('pending'); - }); - }); - - it('should return 401 without auth token', async () => { - await request(httpServer).get('/escrows').expect(401); - }); - }); - - describe('GET /escrows/overview', () => { - async function createOverviewEscrow(params: { - title: string; - amount?: number; - asset?: { code: string; issuer?: string }; - expiresAt?: string; - }): Promise { - const response = await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: params.title, - amount: params.amount ?? 100, - asset: params.asset ?? { code: 'XLM' }, - type: EscrowType.STANDARD, - expiresAt: params.expiresAt, - parties: [{ userId: secondUserId, role: PartyRole.SELLER }], - }) - .expect(201); - - return (response.body as EscrowResponse).id; - } - - it('should filter by role and status', async () => { - const pendingId = await createOverviewEscrow({ - title: 'Overview Pending Escrow', - }); - const completedId = await createOverviewEscrow({ - title: 'Overview Completed Escrow', - }); - - await escrowRepository.update(completedId, { - status: EscrowStatus.COMPLETED, - isReleased: true, - }); - - const depositorCompletedResponse = await request(httpServer) - .get('/escrows/overview?role=depositor&status=completed') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const depositorCompletedBody = - depositorCompletedResponse.body as EscrowOverviewResponse; - const completedEscrow = depositorCompletedBody.data.find( - (item) => item.escrowId === completedId, - ); - - expect(completedEscrow).toBeDefined(); - expect(completedEscrow?.status).toBe('completed'); - expect(completedEscrow?.depositor).toBe(userId); - expect(completedEscrow?.recipient).toBe(secondUserId); - expect(completedEscrow?.totalReleased).toBe(completedEscrow?.totalAmount); - expect(completedEscrow?.remainingAmount).toBe(0); - expect( - depositorCompletedBody.data.some((item) => item.escrowId === pendingId), - ).toBe(false); - - const recipientPendingResponse = await request(httpServer) - .get('/escrows/overview?role=recipient&status=created') - .set('Authorization', `Bearer ${secondAccessToken}`) - .expect(200); - - const recipientPendingBody = - recipientPendingResponse.body as EscrowOverviewResponse; - expect( - recipientPendingBody.data.some((item) => item.escrowId === pendingId), - ).toBe(true); - recipientPendingBody.data.forEach((item) => { - expect(item.status).toBe('pending'); - expect(item.recipient).toBe(secondUserId); - }); - }); - - it('should return accurate pagination metadata and handle out-of-range pages', async () => { - await createOverviewEscrow({ title: 'Overview Pagination 1' }); - await createOverviewEscrow({ title: 'Overview Pagination 2' }); - await createOverviewEscrow({ title: 'Overview Pagination 3' }); - - const pageOneResponse = await request(httpServer) - .get( - '/escrows/overview?page=1&pageSize=2&sortBy=createdAt&sortOrder=desc', - ) - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const pageOneBody = pageOneResponse.body as EscrowOverviewResponse; - expect(pageOneBody.page).toBe(1); - expect(pageOneBody.pageSize).toBe(2); - expect(pageOneBody.totalItems).toBeGreaterThanOrEqual(3); - expect(pageOneBody.totalPages).toBe( - Math.ceil(pageOneBody.totalItems / pageOneBody.pageSize), - ); - expect(pageOneBody.data.length).toBeLessThanOrEqual(2); - - const outOfRangeResponse = await request(httpServer) - .get('/escrows/overview?page=999&pageSize=2') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const outOfRangeBody = outOfRangeResponse.body as EscrowOverviewResponse; - expect(outOfRangeBody.page).toBe(999); - expect(outOfRangeBody.pageSize).toBe(2); - expect(outOfRangeBody.data).toEqual([]); - }); - - it('should sort by created date and deadline', async () => { - const idOld = await createOverviewEscrow({ - title: 'Overview Sort Old', - expiresAt: '2026-06-10T10:00:00.000Z', - }); - const idNew = await createOverviewEscrow({ - title: 'Overview Sort New', - expiresAt: '2026-06-01T10:00:00.000Z', - }); - - await escrowRepository.update(idOld, { - createdAt: new Date('2026-01-01T00:00:00.000Z'), - }); - await escrowRepository.update(idNew, { - createdAt: new Date('2026-01-15T00:00:00.000Z'), - }); - - const createdSortResponse = await request(httpServer) - .get('/escrows/overview?sortBy=createdAt&sortOrder=asc&pageSize=50') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const createdSortBody = - createdSortResponse.body as EscrowOverviewResponse; - const createdIds = createdSortBody.data.map((item) => item.escrowId); - expect(createdIds.indexOf(idOld)).toBeLessThan(createdIds.indexOf(idNew)); - - const deadlineSortResponse = await request(httpServer) - .get('/escrows/overview?sortBy=deadline&sortOrder=asc&pageSize=50') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const deadlineSortBody = - deadlineSortResponse.body as EscrowOverviewResponse; - const deadlineIds = deadlineSortBody.data.map((item) => item.escrowId); - expect(deadlineIds.indexOf(idNew)).toBeLessThan( - deadlineIds.indexOf(idOld), - ); - }); - }); - - describe('GET /escrows/:id', () => { - let escrowId: string; - - beforeAll(async () => { - const response = await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: 'Get Test Escrow', - amount: 50, - parties: [{ userId: secondUserId, role: PartyRole.SELLER }], - }); - escrowId = (response.body as EscrowResponse).id; - }); - - it('should return escrow details for creator', async () => { - const response = await request(httpServer) - .get(`/escrows/${escrowId}`) - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EscrowResponse; - expect(body.id).toBe(escrowId); - expect(body.title).toBe('Get Test Escrow'); - }); - - it('should return escrow details for party', async () => { - const response = await request(httpServer) - .get(`/escrows/${escrowId}`) - .set('Authorization', `Bearer ${secondAccessToken}`) - .expect(200); - - const body = response.body as EscrowResponse; - expect(body.id).toBe(escrowId); - }); - - it('should return 404 for non-existent escrow', async () => { - await request(httpServer) - .get('/escrows/non-existent-id') - .set('Authorization', `Bearer ${accessToken}`) - .expect(404); - }); - }); - - describe('PATCH /escrows/:id', () => { - let escrowId: string; - - beforeEach(async () => { - const response = await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: 'Update Test Escrow', - amount: 75, - parties: [{ userId: secondUserId, role: PartyRole.SELLER }], - }); - escrowId = (response.body as EscrowResponse).id; - }); - - it('should update escrow by creator', async () => { - const response = await request(httpServer) - .patch(`/escrows/${escrowId}`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ title: 'Updated Title' }) - .expect(200); - - const body = response.body as EscrowResponse; - expect(body.title).toBe('Updated Title'); - }); - - it('should return 403 when non-creator tries to update', async () => { - await request(httpServer) - .patch(`/escrows/${escrowId}`) - .set('Authorization', `Bearer ${secondAccessToken}`) - .send({ title: 'Unauthorized Update' }) - .expect(403); - }); - }); - - describe('POST /escrows/:id/cancel', () => { - let escrowId: string; - - beforeEach(async () => { - const response = await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: 'Cancel Test Escrow', - amount: 25, - parties: [{ userId: secondUserId, role: PartyRole.SELLER }], - }); - escrowId = (response.body as EscrowResponse).id; - }); - - it('should cancel escrow by creator', async () => { - const response = await request(httpServer) - .post(`/escrows/${escrowId}/cancel`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ reason: 'Changed my mind' }) - .expect(201); - - const body = response.body as EscrowResponse; - expect(body.status).toBe('cancelled'); - }); - - it('should return 403 when non-creator tries to cancel pending escrow', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/cancel`) - .set('Authorization', `Bearer ${secondAccessToken}`) - .send({ reason: 'Unauthorized cancel' }) - .expect(403); - }); - - it('should return 400 when trying to cancel already cancelled escrow', async () => { - // First cancel - await request(httpServer) - .post(`/escrows/${escrowId}/cancel`) - .set('Authorization', `Bearer ${accessToken}`) - .send({}); - - // Try to cancel again - await request(httpServer) - .post(`/escrows/${escrowId}/cancel`) - .set('Authorization', `Bearer ${accessToken}`) - .send({}) - .expect(400); - }); - }); - - // --------------------------------------------------------------------------- - // Dispute management - // --------------------------------------------------------------------------- - - interface DisputeResponse { - id: string; - escrowId: string; - reason: string; - status: string; - filedByUserId: string; - evidence: string[] | null; - outcome: string | null; - resolutionNotes: string | null; - sellerPercent: string | null; - buyerPercent: string | null; - resolvedByUserId: string | null; - resolvedAt: string | null; - } - - /** Create an escrow with buyer + seller + arbitrator and force it to ACTIVE. */ - const createActiveEscrow = async (): Promise => { - const res = await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: 'Dispute Test Escrow', - description: 'Test description', - amount: 100, - asset: { code: 'XLM' }, - parties: [ - { userId: secondUserId, role: PartyRole.SELLER }, - { userId: arbitratorUserId, role: PartyRole.ARBITRATOR }, - ], - }); - if (res.status !== 201) { - console.log('CREATE ESCROW FAILED:', res.body); - } - expect(res.status).toBe(201); - const id = (res.body as EscrowResponse).id; - await escrowRepository.update(id, { status: EscrowStatus.ACTIVE }); - return id; - }; - - /** Create an ACTIVE escrow and immediately file a dispute on it (buyer). */ - const createDisputedEscrow = async (): Promise => { - const id = await createActiveEscrow(); - await request(httpServer) - .post(`/escrows/${id}/dispute`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ reason: 'Pre-filed dispute for resolution tests' }); - return id; - }; - - describe('POST /escrows/:id/dispute', () => { - let escrowId: string; - - beforeEach(async () => { - escrowId = await createActiveEscrow(); - }); - - it('should allow a buyer to file a dispute', async () => { - const res = await request(httpServer) - .post(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ reason: 'Goods not delivered as described' }) - .expect(201); - - const body = res.body as DisputeResponse; - expect(body).toHaveProperty('id'); - expect(body.escrowId).toBe(escrowId); - expect(body.reason).toBe('Goods not delivered as described'); - expect(body.status).toBe('open'); - expect(body.filedByUserId).toBe(userId); - expect(body.outcome).toBeNull(); - }); - - it('should allow a seller to file a dispute with evidence', async () => { - const evidence = [ - 'https://example.com/screenshot.png', - 'ipfs://QmAbc123', - ]; - const res = await request(httpServer) - .post(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${secondAccessToken}`) - .send({ reason: 'Payment terms violated', evidence }) - .expect(201); - - const body = res.body as DisputeResponse; - expect(body.filedByUserId).toBe(secondUserId); - expect(body.evidence).toEqual(evidence); - }); - - it('should transition escrow status to disputed', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ reason: 'Status transition check' }) - .expect(201); - - const escrowRes = await request(httpServer) - .get(`/escrows/${escrowId}`) - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - expect((escrowRes.body as EscrowResponse).status).toBe('disputed'); - }); - - it('should return 400 when escrow is not active (pending)', async () => { - const pendingRes = await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: 'Pending Escrow', - amount: 50, - parties: [ - { userId: secondUserId, role: PartyRole.SELLER }, - { userId: arbitratorUserId, role: PartyRole.ARBITRATOR }, - ], - }); - const pendingId = (pendingRes.body as EscrowResponse).id; - - await request(httpServer) - .post(`/escrows/${pendingId}/dispute`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ reason: 'Should be rejected' }) - .expect(400); - }); - - it('should return 403 when an arbitrator tries to file a dispute', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .send({ reason: 'Arbitrators mediate, not file' }) - .expect(403); - }); - - it('should return 409 when a duplicate dispute is filed', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ reason: 'First dispute' }) - .expect(201); - - await request(httpServer) - .post(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${secondAccessToken}`) - .send({ reason: 'Second dispute attempt' }) - .expect(409); - }); - - it('should return 400 for missing reason', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${accessToken}`) - .send({}) - .expect(400); - }); - - it('should return 401 without an auth token', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute`) - .send({ reason: 'No token' }) - .expect(401); - }); - }); - - describe('GET /escrows/:id/dispute', () => { - let escrowId: string; - - beforeEach(async () => { - escrowId = await createActiveEscrow(); - await request(httpServer) - .post(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ - reason: 'Disputed delivery', - evidence: ['https://proof.example.com'], - }); - }); - - it('should return dispute details for the filing party (buyer)', async () => { - const res = await request(httpServer) - .get(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = res.body as DisputeResponse; - expect(body).toHaveProperty('id'); - expect(body.escrowId).toBe(escrowId); - expect(body.reason).toBe('Disputed delivery'); - expect(body.status).toBe('open'); - expect(body.evidence).toEqual(['https://proof.example.com']); - expect(body.outcome).toBeNull(); - expect(body.resolvedAt).toBeNull(); - }); - - it('should return dispute details for the seller', async () => { - const res = await request(httpServer) - .get(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${secondAccessToken}`) - .expect(200); - - expect((res.body as DisputeResponse).reason).toBe('Disputed delivery'); - }); - - it('should return dispute details for the arbitrator', async () => { - const res = await request(httpServer) - .get(`/escrows/${escrowId}/dispute`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .expect(200); - - expect((res.body as DisputeResponse).reason).toBe('Disputed delivery'); - }); - - it('should return 404 when no dispute exists for the escrow', async () => { - // Create a fresh active escrow with no dispute - const freshId = await createActiveEscrow(); - - await request(httpServer) - .get(`/escrows/${freshId}/dispute`) - .set('Authorization', `Bearer ${accessToken}`) - .expect(404); - }); - - it('should return 403 for a user who is not a party to the escrow', async () => { - // Create a separate escrow that excludes the arbitrator - const outsiderRes = await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: 'Outsider Test Escrow', - amount: 50, - parties: [{ userId: secondUserId, role: PartyRole.SELLER }], - }); - const outsiderId = (outsiderRes.body as EscrowResponse).id; - await escrowRepository.update(outsiderId, { - status: EscrowStatus.ACTIVE, - }); - await request(httpServer) - .post(`/escrows/${outsiderId}/dispute`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ reason: 'Outsider test' }); - - // arbitratorUserId is NOT a party here — should get 403 - await request(httpServer) - .get(`/escrows/${outsiderId}/dispute`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .expect(403); - }); - }); - - describe('POST /escrows/:id/dispute/resolve', () => { - let escrowId: string; - - beforeEach(async () => { - escrowId = await createDisputedEscrow(); - }); - - it('should resolve a dispute with outcome released_to_seller', async () => { - const res = await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .send({ - outcome: 'released_to_seller', - resolutionNotes: 'Seller provided valid proof of delivery', - }) - .expect(201); - - const body = res.body as DisputeResponse; - expect(body.status).toBe('resolved'); - expect(body.outcome).toBe('released_to_seller'); - expect(body.resolutionNotes).toBe( - 'Seller provided valid proof of delivery', - ); - expect(body.resolvedByUserId).toBe(arbitratorUserId); - expect(body.resolvedAt).not.toBeNull(); - }); - - it('should resolve a dispute with outcome refunded_to_buyer', async () => { - const res = await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .send({ - outcome: 'refunded_to_buyer', - resolutionNotes: 'Seller failed to deliver', - }) - .expect(201); - - const body = res.body as DisputeResponse; - expect(body.status).toBe('resolved'); - expect(body.outcome).toBe('refunded_to_buyer'); - }); - - it('should resolve a dispute with a split outcome', async () => { - const res = await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .send({ - outcome: 'split', - resolutionNotes: 'Partial delivery accepted', - sellerPercent: 60, - buyerPercent: 40, - }) - .expect(201); - - const body = res.body as DisputeResponse; - expect(body.status).toBe('resolved'); - expect(body.outcome).toBe('split'); - expect(Number(body.sellerPercent)).toBe(60); - expect(Number(body.buyerPercent)).toBe(40); - }); - - it('should transition escrow to completed for released_to_seller', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .send({ - outcome: 'released_to_seller', - resolutionNotes: 'Seller wins', - }); - - const escrowRes = await request(httpServer) - .get(`/escrows/${escrowId}`) - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - expect((escrowRes.body as EscrowResponse).status).toBe('completed'); - }); - - it('should transition escrow to cancelled for refunded_to_buyer', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .send({ outcome: 'refunded_to_buyer', resolutionNotes: 'Buyer wins' }); - - const escrowRes = await request(httpServer) - .get(`/escrows/${escrowId}`) - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - expect((escrowRes.body as EscrowResponse).status).toBe('cancelled'); - }); - - it('should return 403 when a buyer tries to resolve', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${accessToken}`) - .send({ - outcome: 'released_to_seller', - resolutionNotes: 'Buyer self-resolving', - }) - .expect(403); - }); - - it('should return 403 when a seller tries to resolve', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${secondAccessToken}`) - .send({ - outcome: 'refunded_to_buyer', - resolutionNotes: 'Seller self-resolving', - }) - .expect(403); - }); - - it('should return 422 when split outcome is missing percentages', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .send({ outcome: 'split', resolutionNotes: 'Forgot percentages' }) - .expect(422); - }); - - it('should return 422 when split percentages do not sum to 100', async () => { - await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .send({ - outcome: 'split', - resolutionNotes: 'Bad math', - sellerPercent: 60, - buyerPercent: 30, - }) - .expect(422); - }); - - it('should return 400 when trying to resolve an already-resolved dispute', async () => { - // First resolution succeeds - await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .send({ - outcome: 'released_to_seller', - resolutionNotes: 'First resolution', - }) - .expect(201); - - // Second attempt: escrow is no longer DISPUTED → 400 - await request(httpServer) - .post(`/escrows/${escrowId}/dispute/resolve`) - .set('Authorization', `Bearer ${arbitratorAccessToken}`) - .send({ - outcome: 'refunded_to_buyer', - resolutionNotes: 'Second attempt', - }) - .expect(400); - }); - }); -}); diff --git a/apps/backend/test/e2e/events.e2e-spec.ts b/apps/backend/test/e2e/events.e2e-spec.ts deleted file mode 100644 index 74a6a355..00000000 --- a/apps/backend/test/e2e/events.e2e-spec.ts +++ /dev/null @@ -1,366 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication, ValidationPipe } from '@nestjs/common'; -import request from 'supertest'; -import type { Server } from 'http'; -import { AppModule } from '../../src/app.module'; -import { createTestApp } from '../setup/test-app.factory'; -import { Keypair } from 'stellar-sdk'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { RefreshToken } from '../../src/modules/user/entities/refresh-token.entity'; -import { User } from '../../src/modules/user/entities/user.entity'; -import { Escrow } from '../../src/modules/escrow/entities/escrow.entity'; -import { - Party, - PartyRole, -} from '../../src/modules/escrow/entities/party.entity'; -import { Condition } from '../../src/modules/escrow/entities/condition.entity'; -import { EscrowEvent } from '../../src/modules/escrow/entities/escrow-event.entity'; -import { AllowedAsset } from '../../src/modules/assets/entities/allowed-asset.entity'; -import { DataSource } from 'typeorm'; - -function createMockKeypair(): Keypair { - return Keypair.random(); -} - -describe('Events (e2e)', () => { - let app: INestApplication; - let httpServer: Server; - let testKeypair: Keypair; - let testWalletAddress: string; - let accessToken: string; - - let secondKeypair: Keypair; - let secondWalletAddress: string; - let secondAccessToken: string; - let secondUserId: string; - - let escrowId: string; - - beforeAll(async () => { - testKeypair = createMockKeypair(); - testWalletAddress = testKeypair.publicKey(); - - secondKeypair = createMockKeypair(); - secondWalletAddress = secondKeypair.publicKey(); - - app = await createTestApp(); - httpServer = app.getHttpServer() as Server; - - const allowedAssetRepository = app - .get(DataSource) - .getRepository(AllowedAsset); - await allowedAssetRepository.save({ - code: 'XLM', - displayName: 'Stellar Lumens', - decimals: 7, - active: true, - }); - - // Authenticate first user - const challengeResponse = await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: testWalletAddress }); - - const message = (challengeResponse.body as { message: string }).message; - const signature = testKeypair.sign(Buffer.from(message)).toString('hex'); - - const verifyResponse = await request(httpServer).post('/auth/verify').send({ - walletAddress: testWalletAddress, - signature: signature, - publicKey: testWalletAddress, - }); - - accessToken = (verifyResponse.body as { accessToken: string }).accessToken; - - // Authenticate second user - const challenge2 = await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: secondWalletAddress }); - - const message2 = (challenge2.body as { message: string }).message; - const signature2 = secondKeypair - .sign(Buffer.from(message2)) - .toString('hex'); - - const verify2 = await request(httpServer).post('/auth/verify').send({ - walletAddress: secondWalletAddress, - signature: signature2, - publicKey: secondWalletAddress, - }); - - secondAccessToken = (verify2.body as { accessToken: string }).accessToken; - - const me2 = await request(httpServer) - .get('/auth/me') - .set('Authorization', `Bearer ${secondAccessToken}`); - secondUserId = (me2.body as { id: string }).id; - - // Create an escrow for testing - const escrowResponse = await request(httpServer) - .post('/escrows') - .set('Authorization', `Bearer ${accessToken}`) - .send({ - title: 'Test Escrow for Events', - description: 'Test description', - amount: 100, - asset: { code: 'XLM' }, - parties: [{ userId: secondUserId, role: PartyRole.SELLER }], - }); - - escrowId = (escrowResponse.body as { id: string }).id; - }); - - afterAll(async () => { - if (app) { - await app.close(); - } - }); - - interface EventResponse { - id: string; - escrowId: string; - eventType: string; - actorId?: string; - data?: Record; - createdAt: string; - escrow?: { - id: string; - title: string; - amount: number; - asset: string; - status: string; - }; - actor?: { - walletAddress?: string; - }; - } - - interface EventsListResponse { - data: EventResponse[]; - total: number; - page: number; - limit: number; - } - - describe('GET /events', () => { - it('should return user events with default pagination', async () => { - const response = await request(httpServer) - .get('/events') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EventsListResponse; - expect(body).toHaveProperty('data'); - expect(body).toHaveProperty('total'); - expect(body).toHaveProperty('page'); - expect(body).toHaveProperty('limit'); - expect(body.page).toBe(1); - expect(body.limit).toBe(10); - }); - - it('should return events with custom pagination', async () => { - const response = await request(httpServer) - .get('/events?page=2&limit=5') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EventsListResponse; - expect(body.page).toBe(2); - expect(body.limit).toBe(5); - }); - - it('should filter events by event type', async () => { - const response = await request(httpServer) - .get('/events?eventType=created') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EventsListResponse; - expect(body.data).toHaveLength(1); - expect(body.data[0].eventType).toBe('created'); - }); - - it('should filter events by date range', async () => { - const now = new Date().toISOString(); - const yesterday = new Date( - Date.now() - 24 * 60 * 60 * 1000, - ).toISOString(); - - const response = await request(httpServer) - .get(`/events?dateFrom=${yesterday}&dateTo=${now}`) - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EventsListResponse; - expect(body.data).toHaveLength(1); - }); - - it('should sort events by createdAt descending by default', async () => { - const response = await request(httpServer) - .get('/events') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EventsListResponse; - if (body.data.length > 1) { - const firstEvent = new Date(body.data[0].createdAt); - const secondEvent = new Date(body.data[1].createdAt); - expect(firstEvent.getTime()).toBeGreaterThanOrEqual( - secondEvent.getTime(), - ); - } - }); - - it('should sort events by createdAt ascending', async () => { - const response = await request(httpServer) - .get('/events?sortOrder=ASC') - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EventsListResponse; - if (body.data.length > 1) { - const firstEvent = new Date(body.data[0].createdAt); - const secondEvent = new Date(body.data[1].createdAt); - expect(firstEvent.getTime()).toBeLessThanOrEqual(secondEvent.getTime()); - } - }); - - it('should return 401 without auth token', async () => { - await request(httpServer).get('/events').expect(401); - }); - - it('should return empty array for user with no events', async () => { - const thirdKeypair = createMockKeypair(); - const thirdWalletAddress = thirdKeypair.publicKey(); - - const challenge3 = await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: thirdWalletAddress }); - - const message3 = (challenge3.body as { message: string }).message; - const signature3 = thirdKeypair - .sign(Buffer.from(message3)) - .toString('hex'); - - const verify3 = await request(httpServer).post('/auth/verify').send({ - walletAddress: thirdWalletAddress, - signature: signature3, - publicKey: thirdWalletAddress, - }); - - const thirdAccessToken = (verify3.body as { accessToken: string }) - .accessToken; - - const response = await request(httpServer) - .get('/events') - .set('Authorization', `Bearer ${thirdAccessToken}`) - .expect(200); - - const body = response.body as EventsListResponse; - expect(body.data).toHaveLength(0); - expect(body.total).toBe(0); - }); - }); - - describe('GET /escrows/:id/events', () => { - it('should return events for specific escrow', async () => { - const response = await request(httpServer) - .get(`/escrows/${escrowId}/events`) - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EventsListResponse; - expect(body.data).toHaveLength(1); - expect(body.data[0].escrowId).toBe(escrowId); - }); - - it('should return 403 for unauthorized user', async () => { - const thirdKeypair = createMockKeypair(); - const thirdWalletAddress = thirdKeypair.publicKey(); - - const challenge3 = await request(httpServer) - .post('/auth/challenge') - .send({ walletAddress: thirdWalletAddress }); - - const message3 = (challenge3.body as { message: string }).message; - const signature3 = thirdKeypair - .sign(Buffer.from(message3)) - .toString('hex'); - - const verify3 = await request(httpServer).post('/auth/verify').send({ - walletAddress: thirdWalletAddress, - signature: signature3, - publicKey: thirdWalletAddress, - }); - - const thirdAccessToken = (verify3.body as { accessToken: string }) - .accessToken; - - await request(httpServer) - .get(`/escrows/${escrowId}/events`) - .set('Authorization', `Bearer ${thirdAccessToken}`) - .expect(403); - }); - - it('should return 404 for non-existent escrow', async () => { - await request(httpServer) - .get('/escrows/invalid-id/events') - .set('Authorization', `Bearer ${accessToken}`) - .expect(404); - }); - - it('should return events with pagination', async () => { - const response = await request(httpServer) - .get(`/escrows/${escrowId}/events?page=1&limit=5`) - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EventsListResponse; - expect(body.page).toBe(1); - expect(body.limit).toBe(5); - }); - - it('should include escrow details in response', async () => { - const response = await request(httpServer) - .get(`/escrows/${escrowId}/events`) - .set('Authorization', `Bearer ${accessToken}`) - .expect(200); - - const body = response.body as EventsListResponse; - const event = body.data[0]; - expect(event.escrow).toBeDefined(); - expect(event.escrow?.id).toBe(escrowId); - expect(event.escrow?.title).toBe('Test Escrow for Events'); - }); - }); - - describe('Event Filtering and Validation', () => { - it('should reject invalid UUID for escrowId', async () => { - await request(httpServer) - .get('/events?escrowId=invalid-uuid') - .set('Authorization', `Bearer ${accessToken}`) - .expect(400); - }); - - it('should reject invalid event type', async () => { - await request(httpServer) - .get('/events?eventType=invalid_type') - .set('Authorization', `Bearer ${accessToken}`) - .expect(400); - }); - - it('should reject invalid date format', async () => { - await request(httpServer) - .get('/events?dateFrom=invalid-date') - .set('Authorization', `Bearer ${accessToken}`) - .expect(400); - }); - - it('should reject invalid pagination values', async () => { - await request(httpServer) - .get('/events?page=0&limit=101') - .set('Authorization', `Bearer ${accessToken}`) - .expect(400); - }); - }); -}); diff --git a/apps/backend/test/e2e/ipfs-metadata.e2e-spec.ts b/apps/backend/test/e2e/ipfs-metadata.e2e-spec.ts deleted file mode 100644 index 6e35017c..00000000 --- a/apps/backend/test/e2e/ipfs-metadata.e2e-spec.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication } from '@nestjs/common'; -import request from 'supertest'; -import { AppModule } from '../../src/app.module'; - -describe('IPFS Metadata Endpoints (Integration)', () => { - let app: INestApplication; - let authToken: string; - let escrowId: string; - - beforeAll(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [AppModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - - // Login and get auth token - const loginResponse = await request(app.getHttpServer()) - .post('/auth/login') - .send({ - email: 'test@example.com', - password: 'password123', - }); - - authToken = loginResponse.body.accessToken; - - // Create a test escrow - const escrowResponse = await request(app.getHttpServer()) - .post('/escrows') - .set('Authorization', `Bearer ${authToken}`) - .send({ - title: 'Test Escrow', - amount: 100, - asset: { code: 'XLM' }, - parties: [ - { userId: 'user-1', role: 'buyer' }, - { userId: 'user-2', role: 'seller' }, - ], - conditions: [ - { - description: 'Test condition', - type: 'delivery', - }, - ], - expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), - }); - - escrowId = escrowResponse.body.id; - }); - - afterAll(async () => { - await app.close(); - }); - - describe('GET /escrows/:id/metadata', () => { - it('should return 400 when no IPFS metadata exists', () => { - return request(app.getHttpServer()) - .get(`/escrows/${escrowId}/metadata`) - .set('Authorization', `Bearer ${authToken}`) - .expect(400); - }); - }); - - describe('POST /escrows/:id/metadata/pin', () => { - it('should pin metadata to IPFS (admin only)', async () => { - const response = await request(app.getHttpServer()) - .post(`/escrows/${escrowId}/metadata/pin`) - .set('Authorization', `Bearer ${authToken}`) - .expect(201); - - expect(response.body).toHaveProperty('cid'); - expect(response.body).toHaveProperty('metadataHash'); - }); - - it('should fail for non-admin users', async () => { - // Create a non-admin user token - const nonAdminResponse = await request(app.getHttpServer()) - .post('/auth/login') - .send({ - email: 'nonadmin@example.com', - password: 'password123', - }); - - const nonAdminToken = nonAdminResponse.body.accessToken; - - await request(app.getHttpServer()) - .post(`/escrows/${escrowId}/metadata/pin`) - .set('Authorization', `Bearer ${nonAdminToken}`) - .expect(403); - }); - }); - - describe('GET /escrows/:id/metadata/verify', () => { - it('should verify metadata integrity after pinning', async () => { - // First pin the metadata - await request(app.getHttpServer()) - .post(`/escrows/${escrowId}/metadata/pin`) - .set('Authorization', `Bearer ${authToken}`); - - // Then verify it - const response = await request(app.getHttpServer()) - .get(`/escrows/${escrowId}/metadata/verify`) - .set('Authorization', `Bearer ${authToken}`) - .expect(200); - - expect(response.body).toHaveProperty('isValid'); - expect(response.body).toHaveProperty('computedHash'); - expect(response.body).toHaveProperty('storedHash'); - expect(response.body).toHaveProperty('metadata'); - }); - }); - - describe('GET /escrows/:id/metadata (after pinning)', () => { - it('should return metadata from IPFS after pinning', async () => { - // First pin the metadata - await request(app.getHttpServer()) - .post(`/escrows/${escrowId}/metadata/pin`) - .set('Authorization', `Bearer ${authToken}`); - - // Then retrieve it - const response = await request(app.getHttpServer()) - .get(`/escrows/${escrowId}/metadata`) - .set('Authorization', `Bearer ${authToken}`) - .expect(200); - - expect(response.body).toHaveProperty('escrowId'); - expect(response.body).toHaveProperty('buyer'); - expect(response.body).toHaveProperty('seller'); - expect(response.body).toHaveProperty('amount'); - expect(response.body).toHaveProperty('status'); - expect(response.body).toHaveProperty('version'); - }); - }); -}); diff --git a/apps/backend/test/jest-e2e.json b/apps/backend/test/jest-e2e.json deleted file mode 100644 index d557a921..00000000 --- a/apps/backend/test/jest-e2e.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "moduleFileExtensions": ["js", "json", "ts"], - "rootDir": ".", - "testEnvironment": "node", - "testRegex": ".e2e-spec.ts$", - "transform": { - "^.+\\.(t|j)s$": "ts-jest" - }, - "moduleNameMapper": { - "^src/(.*)$": "/../src/$1" - }, - "setupFilesAfterEnv": ["/setup/setup-e2e.ts"], - "testTimeout": 60000 -} diff --git a/apps/backend/test/minimal.e2e-spec.ts b/apps/backend/test/minimal.e2e-spec.ts deleted file mode 100644 index 3f4aa531..00000000 --- a/apps/backend/test/minimal.e2e-spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { INestApplication, Module } from '@nestjs/common'; - -@Module({}) -class MinimalModule {} - -describe('Minimal (e2e)', () => { - let app: INestApplication; - - beforeEach(async () => { - const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [MinimalModule], - }).compile(); - - app = moduleFixture.createNestApplication(); - await app.init(); - }); - - it('should be defined', () => { - expect(app).toBeDefined(); - }); - - afterEach(async () => { - await app.close(); - }); -}); diff --git a/apps/backend/test/setup/mocks/blockchain.mock.ts b/apps/backend/test/setup/mocks/blockchain.mock.ts deleted file mode 100644 index 62ec54fc..00000000 --- a/apps/backend/test/setup/mocks/blockchain.mock.ts +++ /dev/null @@ -1,22 +0,0 @@ -export interface BlockchainMock { - getBalance: (address: string) => Promise; - sendTransaction: (tx: Record) => Promise<{ hash: string }>; - getTransactionStatus: (hash: string) => Promise<'pending' | 'confirmed'>; -} - -export const blockchainMock: BlockchainMock = { - getBalance: (address: string): Promise => { - void address; - return Promise.resolve(1000); // mock balance - }, - - sendTransaction: (tx: Record): Promise<{ hash: string }> => { - void tx; - return Promise.resolve({ hash: 'mock-tx-hash' }); - }, - - getTransactionStatus: (hash: string): Promise<'pending' | 'confirmed'> => { - void hash; - return Promise.resolve('confirmed'); - }, -}; diff --git a/apps/backend/test/setup/mocks/stellar.mock.ts b/apps/backend/test/setup/mocks/stellar.mock.ts deleted file mode 100644 index 388fe24e..00000000 --- a/apps/backend/test/setup/mocks/stellar.mock.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface StellarMock { - createEscrow: () => Promise<{ escrowId: string }>; - releaseFunds: () => Promise; -} - -export const stellarMock: StellarMock = { - createEscrow: (): Promise<{ escrowId: string }> => { - return Promise.resolve({ escrowId: 'mock-escrow-id' }); - }, - - releaseFunds: (): Promise => { - return Promise.resolve(true); - }, -}; diff --git a/apps/backend/test/setup/seed.ts b/apps/backend/test/setup/seed.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/backend/test/setup/setup-e2e.ts b/apps/backend/test/setup/setup-e2e.ts deleted file mode 100644 index b7f87cd5..00000000 --- a/apps/backend/test/setup/setup-e2e.ts +++ /dev/null @@ -1,4 +0,0 @@ -process.env.DATABASE_PATH = ':memory:'; -process.env.NODE_ENV = 'test'; -process.env.JWT_SECRET = 'test-secret'; -process.env.STELLAR_CONTRACT_ID = 'CACVKL567TEST'; diff --git a/apps/backend/test/setup/test-app.factory.ts b/apps/backend/test/setup/test-app.factory.ts deleted file mode 100644 index bd2c5391..00000000 --- a/apps/backend/test/setup/test-app.factory.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { Test, TestingModuleBuilder } from '@nestjs/testing'; -import { INestApplication, ValidationPipe } from '@nestjs/common'; -import { AppModule } from './../../src/app.module'; -import { StellarService } from './../../src/services/stellar.service'; -import { SorobanClientService } from './../../src/services/stellar/soroban-client.service'; -import { StellarEventListenerService } from './../../src/modules/stellar/services/stellar-event-listener.service'; -import { Keypair } from '@stellar/stellar-sdk'; - -const mockStellarService = { - isValidPublicKey: (_key: string) => true, - isValidSecretKey: (_key: string) => true, - createKeypair: () => { - return Keypair.random(); - }, - getAccount: jest.fn().mockResolvedValue({ - id: 'mock-account', - sequenceNumber: () => '1', - balances: [{ asset_type: 'native', balance: '1000000' }], - }), - validateAsset: jest.fn().mockResolvedValue(true), - buildTransaction: jest.fn().mockResolvedValue({ - hash: () => Buffer.from('mock-hash-hex-code-here', 'hex'), - }), - submitTransaction: jest.fn().mockResolvedValue({ - hash: 'mock-tx-hash-here', - }), - streamTransactions: jest.fn().mockReturnValue({ - close: () => {}, - }), - checkTransactionStatus: jest.fn().mockResolvedValue({ - successful: true, - }), -}; - -const mockSorobanClientService = { - getEscrow: jest.fn().mockResolvedValue({ - status: 'active', - amount: '100', - depositor: 'GD3...etc', - recipient: 'GD4...etc', - }), - decodeContractError: jest.fn().mockReturnValue('MockError'), - getContractId: jest.fn().mockReturnValue('CACVKL567TEST'), - getRpc: jest.fn().mockReturnValue({ - getLatestLedger: jest.fn().mockResolvedValue({ sequence: 100 }), - getEvents: jest.fn().mockResolvedValue({ events: [] }), - }), -}; - -const mockStellarEventListenerService = { - onModuleInit: jest.fn().mockResolvedValue(undefined), - onModuleDestroy: jest.fn().mockResolvedValue(undefined), - startEventListener: jest.fn().mockResolvedValue(undefined), - stopEventListener: jest.fn().mockResolvedValue(undefined), - syncFromLedger: jest.fn().mockResolvedValue(undefined), - getSyncStatus: jest.fn().mockReturnValue({ - isRunning: false, - lastProcessedLedger: 0, - reconnectAttempts: 0, - }), -}; - -export async function createTestApp( - configureBuilder?: (builder: TestingModuleBuilder) => TestingModuleBuilder, - configureApp?: (app: INestApplication) => void, -): Promise { - let builder = Test.createTestingModule({ - imports: [AppModule], - }) - .overrideProvider(StellarService) - .useValue(mockStellarService) - .overrideProvider(SorobanClientService) - .useValue(mockSorobanClientService) - .overrideProvider(StellarEventListenerService) - .useValue(mockStellarEventListenerService); - - if (configureBuilder) { - builder = configureBuilder(builder); - } - - const moduleRef = await builder.compile(); - - const app = moduleRef.createNestApplication(); - if (configureApp) { - configureApp(app); - } else { - app.useGlobalPipes(new ValidationPipe({ transform: true })); - } - await app.init(); - - return app; -} diff --git a/apps/backend/test/setup/test-db.ts b/apps/backend/test/setup/test-db.ts deleted file mode 100644 index c8ab8a07..00000000 --- a/apps/backend/test/setup/test-db.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { DataSource } from 'typeorm'; - -export async function resetDatabase(dataSource: DataSource) { - const entities = dataSource.entityMetadatas; - - for (const entity of entities) { - const repo = dataSource.getRepository(entity.name); - await repo.query(`DELETE FROM ${entity.tableName}`); - } -} diff --git a/apps/backend/test/test-admin-module.ts b/apps/backend/test/test-admin-module.ts deleted file mode 100644 index 443adff5..00000000 --- a/apps/backend/test/test-admin-module.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Test } from '@nestjs/testing'; -import { AdminModule } from '../src/modules/admin/admin.module'; - -async function bootstrap() { - try { - const moduleFixture = await Test.createTestingModule({ - imports: [AdminModule], - }).compile(); - console.log('AdminModule compiled successfully'); - await moduleFixture.close(); - } catch (error) { - console.error('Failed to compile AdminModule:', error); - process.exit(1); - } -} - -void bootstrap(); diff --git a/apps/backend/test_results.json b/apps/backend/test_results.json deleted file mode 100644 index d5a95d2a..00000000 Binary files a/apps/backend/test_results.json and /dev/null differ diff --git a/apps/backend/tsconfig.build.json b/apps/backend/tsconfig.build.json deleted file mode 100644 index 64f86c6b..00000000 --- a/apps/backend/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} diff --git a/apps/backend/tsconfig.json b/apps/backend/tsconfig.json deleted file mode 100644 index 286103e8..00000000 --- a/apps/backend/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "moduleResolution": "node", - "esModuleInterop": true, - "isolatedModules": true, - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": true, - "forceConsistentCasingInFileNames": true, - "noImplicitAny": false, - "strictBindCallApply": false, - "noFallthroughCasesInSwitch": false, - "lib": ["es2021"] - } -} diff --git a/apps/frontend/README.md b/apps/frontend/README.md deleted file mode 100644 index e215bc4c..00000000 --- a/apps/frontend/README.md +++ /dev/null @@ -1,36 +0,0 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/apps/frontend/app/(protected)/profile/page.tsx b/apps/frontend/app/(protected)/profile/page.tsx deleted file mode 100644 index 7cda4dd7..00000000 --- a/apps/frontend/app/(protected)/profile/page.tsx +++ /dev/null @@ -1,332 +0,0 @@ -'use client'; - -import React, { useState, useEffect, useMemo, useCallback } from 'react'; -import { - User, Wallet, Calendar, ShieldCheck, Award, - FileText, CheckCircle2, AlertTriangle, TrendingUp, - DollarSign, Edit3, Check, X, Copy, ExternalLink -} from 'lucide-react'; - -interface EscrowRecord { - id: string; - title: string; - status: 'PENDING' | 'ACTIVE' | 'COMPLETED' | 'DISPUTED' | 'REFUNDED'; - amount: number; - createdAt: string; -} - -interface ProfileData { - walletAddress: string; - displayName: string; - createdAt: string; - role: 'USER' | 'ADMIN' | 'VERIFIER'; - stats: { - totalCreated: number; - totalParticipated: number; - completedCount: number; - disputeCount: number; - totalVolume: number; - }; - recentHistory: EscrowRecord[]; -} - -export default function ProfilePage() { - const [profile, setProfile] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const [isEditingName, setIsEditingName] = useState(false); - const [nameInput, setNameInput] = useState(''); - const [copied, setCopied] = useState(false); - const [isSavingName, setIsSavingName] = useState(false); - - // 1. Data Hydration Ingestion Matrix - const fetchProfile = useCallback(async () => { - try { - const response = await fetch('/api/user/profile'); - if (response.ok) { - const data = await response.json(); - setProfile(data); - setNameInput(data.displayName || ''); - } - } catch (err) { - console.error('Failed to resolve profile dataset registries:', err); - } finally { - setIsLoading(false); - } - }, []); - - useEffect(() => { - fetchProfile(); - }, [fetchProfile]); - - // 2. Inline Interactive Name Upstream Dispatches - const handleSaveName = async () => { - if (!nameInput.trim() || isSavingName) return; - setIsSavingName(true); - try { - const response = await fetch('/api/user/profile/update', { - method: 'PATCH', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ displayName: nameInput.trim() }), - }); - if (response.ok) { - setProfile(prev => prev ? { ...prev, displayName: nameInput.trim() } : null); - setIsEditingName(false); - } - } catch (err) { - console.error('Failed to commit profile display name mutations:', err); - } finally { - setIsSavingName(false); - } - }; - - const handleCopyWallet = () => { - if (!profile?.walletAddress) return; - navigator.clipboard.writeText(profile.walletAddress); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - // 3. Mathematical Algorithmic Reputation Computations - const computedMetrics = useMemo(() => { - if (!profile) return null; - - const { totalCreated, totalParticipated, completedCount, disputeCount, totalVolume } = profile.stats; - const totalParticipation = totalCreated + totalParticipated; - - // Averages and Success Baselines - const successRate = totalParticipation > 0 ? (completedCount / totalParticipation) * 100 : 100; - const avgAmount = completedCount > 0 ? totalVolume / completedCount : 0; - - // Account Age Scoring Framework (Max 100 base bounds mapped across 1 year milestone rules) - const accountAgeDays = Math.floor((Date.now() - new Date(profile.createdAt).getTime()) / (1000 * 60 * 60 * 24)); - const ageScore = Math.min((accountAgeDays / 365) * 100, 100); - - // Inverse Dispute Burden - const disputeRate = totalParticipation > 0 ? (disputeCount / totalParticipation) : 0; - const inverseDisputeScore = Math.max((1 - disputeRate) * 100, 0); - - // Volume Multiplier Bracket Scoring (ceiling cap normalized at $50k) - const volumeScore = Math.min((totalVolume / 50000) * 100, 100); - - // Weighted Formula Combination Checklist: Success (40%) + Inverse Dispute (30%) + Age (15%) + Volume (15%) - const trustScore = Math.round( - (successRate * 0.40) + - (inverseDisputeScore * 0.30) + - (ageScore * 0.15) + - (volumeScore * 0.15) - ); - - // Tier Classification Engine Selection Boundaries - let tier: 'Bronze' | 'Silver' | 'Gold' | 'Platinum' = 'Bronze'; - let tierColor = 'bg-amber-900/30 border-amber-800 text-amber-500'; - if (trustScore >= 90) { - tier = 'Platinum'; - tierColor = 'bg-cyan-950/40 border-cyan-800 text-cyan-400'; - } else if (trustScore >= 75) { - tier = 'Gold'; - tierColor = 'bg-yellow-950/40 border-yellow-800 text-yellow-400'; - } else if (trustScore >= 50) { - tier = 'Silver'; - tierColor = 'bg-slate-800/80 border-slate-700 text-slate-300'; - } - - return { - successRate: Math.round(successRate), - avgAmount: Math.round(avgAmount), - trustScore, - tier, - tierColor - }; - }, [profile]); - - // LOADING SKELETON PLACEHOLDERS FLUID TRANSITIONS - if (isLoading) { - return ( -
-
-
-
-
-
-
- ); - } - - if (!profile || !computedMetrics) return null; - - return ( -
- - {/* HEADER META CARD: MULTI-DEVICE RESPONSIVE WRAPPER */} -
-
-
- -
- -
-
- {isEditingName ? ( -
- setNameInput(e.target.value)} - className="bg-slate-950 border border-slate-700 rounded px-2 py-0.5 text-sm text-white focus:outline-none focus:border-blue-500" - maxLength={25} - disabled={isSavingName} - /> - - -
- ) : ( - <> -

{profile.displayName || 'Anonymous User'}

- - - )} -
- - {/* Wallet Quick Copy Sub-row */} -
- - {profile.walletAddress.slice(0, 6)}...{profile.walletAddress.slice(-6)} - -
-
-
- - {/* System Authorized Role Badge Block */} -
- - {profile.role} Badge - -
- - Joined {new Date(profile.createdAt).toLocaleDateString(undefined, { month: 'short', year: 'numeric' })} -
-
-
- - {/* CORE TWO-COLUMN MAIN WORKSPACE */} -
- - {/* LEFT COLUMN: TRUST AND REPUTATION METRICS (1-span) */} -
- - {/* Trust Score Radial Dial Summary Module */} -
-

Escrow Reputation Bracket

- -
- {/* Outer visual feedback ring */} -
-
- {computedMetrics.trustScore} - Trust Index -
-
- -
- {computedMetrics.tier} Tier -
-
- - {/* System Isolated Dispute Logs (Visible only to context rules targets) */} -
-

- Administrative Auditing -

-
- Lifetime Disputes Handled - 0 ? 'text-rose-400' : 'text-slate-300'}`}> - {profile.stats.disputeCount} incidents - -
-
-
- - {/* RIGHT COLUMN: PORTFOLIO AGGREGATES AND LIVE ESCROW HISTORY (2-spans) */} -
- - {/* Numerical Aggregate Statistics Dashboard Row */} -
-
- - Total Escrows - - {profile.stats.totalCreated + profile.stats.totalParticipated} -
-
- - Completed - - {profile.stats.completedCount} -
-
- - Success Bound - - {computedMetrics.successRate}% -
-
- - Gross Volume - - ${profile.stats.totalVolume.toLocaleString()} -
-
- - Median Ticket Size - - ${computedMetrics.avgAmount.toLocaleString()} -
-
- - {/* RECENT HISTORICAL ACTIVITY LOG TABLE PREVIEW */} -
-

Recent Portfolio Activity

- - {profile.recentHistory.length === 0 ? ( -

Zero historical transaction entries reported.

- ) : ( -
- {profile.recentHistory.map((escrow) => ( -
-
- {escrow.title} - - {new Date(escrow.createdAt).toLocaleDateString(undefined, { dateStyle: 'medium' })} - -
- -
-
- ${escrow.amount.toLocaleString()} - - {escrow.status} - -
- -
-
- ))} -
- )} -
-
- -
-
- ); -} \ No newline at end of file diff --git a/apps/frontend/app/admin/analytics/page.tsx b/apps/frontend/app/admin/analytics/page.tsx deleted file mode 100644 index 1f1c51d9..00000000 --- a/apps/frontend/app/admin/analytics/page.tsx +++ /dev/null @@ -1,277 +0,0 @@ -'use client'; - -import React, { useState, useEffect } from 'react'; -import { BarChart3, PieChart, TrendingUp, Users, Loader2 } from 'lucide-react'; -import { AdminService } from '@/services/admin'; -import { IPlatformStats } from '@/types/admin'; - -// ── Date Range Selector ──────────────────────────────────────────────────── - -type Range = '7d' | '30d' | '90d'; - -const RANGES: { label: string; value: Range }[] = [ - { label: '7 days', value: '7d' }, - { label: '30 days', value: '30d' }, - { label: '90 days', value: '90d' }, -]; - -// ── Escrow Volume Bar Chart (CSS-based) ──────────────────────────────────── - -const VOLUME_DATA: Record = { - '7d': [ - { label: 'Mon', value: 42 }, { label: 'Tue', value: 67 }, { label: 'Wed', value: 55 }, - { label: 'Thu', value: 80 }, { label: 'Fri', value: 91 }, { label: 'Sat', value: 38 }, { label: 'Sun', value: 29 }, - ], - '30d': [ - { label: 'W1', value: 198 }, { label: 'W2', value: 240 }, { label: 'W3', value: 312 }, { label: 'W4', value: 278 }, - ], - '90d': [ - { label: 'Jan', value: 480 }, { label: 'Feb', value: 610 }, { label: 'Mar', value: 850 }, - ], -}; - -function VolumeChart({ range }: { range: Range }) { - const data = VOLUME_DATA[range]; - const max = Math.max(...data.map((d) => d.value)); - - return ( -
-

- - Escrow Creation Volume -

-

Number of escrows created per period

-
- {data.map((d, i) => { - const pct = (d.value / max) * 100; - return ( -
- {d.value} -
-
-
- {d.label} -
- ); - })} -
-
- ); -} - -// ── Status Distribution Donut (CSS conic-gradient) ───────────────────────── - -const STATUS_COLORS: Record = { - Active: '#3b82f6', - Completed: '#10b981', - Disputed: '#f59e0b', - Expired: '#ef4444', -}; - -function StatusDonut({ stats }: { stats: IPlatformStats }) { - const total = stats.escrows.total || 1; - const slices = [ - { label: 'Active', value: stats.escrows.active, color: STATUS_COLORS.Active }, - { label: 'Completed', value: stats.escrows.completed, color: STATUS_COLORS.Completed }, - { label: 'Disputed', value: Math.round(total * 0.03), color: STATUS_COLORS.Disputed }, - { label: 'Expired', value: total - stats.escrows.active - stats.escrows.completed - Math.round(total * 0.03), color: STATUS_COLORS.Expired }, - ]; - - let accumulated = 0; - const gradient = slices - .map((s) => { - const pct = (s.value / total) * 100; - const from = accumulated; - accumulated += pct; - return `${s.color} ${from.toFixed(1)}% ${accumulated.toFixed(1)}%`; - }) - .join(', '); - - return ( -
-

- - Escrow Status Distribution -

-
-
-
    - {slices.map((s) => ( -
  • - - {s.label} - - {((s.value / total) * 100).toFixed(1)}% - -
  • - ))} -
-
-
- ); -} - -// ── Top Users Table ──────────────────────────────────────────────────────── - -const MOCK_TOP_USERS = Array.from({ length: 10 }, (_, i) => ({ - rank: i + 1, - wallet: `G${String.fromCharCode(65 + i)}${'ABCDEFGHIJK234567'.repeat(3).slice(0, 54)}`, - escrows: 120 - i * 11, - volume: (500000 - i * 45000).toLocaleString(), -})); - -type SortKey = 'escrows' | 'volume'; - -function TopUsersTable() { - const [sort, setSort] = useState('escrows'); - const sorted = [...MOCK_TOP_USERS].sort((a, b) => - sort === 'escrows' ? b.escrows - a.escrows : parseInt(b.volume.replace(/,/g, '')) - parseInt(a.volume.replace(/,/g, '')), - ); - - const thClass = 'text-left text-xs text-gray-500 font-medium py-2 px-3 uppercase tracking-wider cursor-pointer hover:text-gray-300'; - - return ( -
-

- - Top 10 Users by Volume -

-
- - - - - - - - - - - {sorted.map((u) => ( - - - - - - - ))} - -
#Wallet setSort('escrows')}> - Escrows {sort === 'escrows' && '↓'} - setSort('volume')}> - Volume (XLM) {sort === 'volume' && '↓'} -
{u.rank} - {u.wallet.slice(0, 8)}…{u.wallet.slice(-4)} - {u.escrows}{u.volume}
-
-
- ); -} - -// ── Dispute Metrics ──────────────────────────────────────────────────────── - -function DisputeMetrics({ stats }: { stats: IPlatformStats }) { - const total = stats.escrows.total || 1; - const disputed = Math.round(total * 0.03); - const disputeRate = ((disputed / total) * 100).toFixed(1); - - return ( -
-

- - Dispute Metrics -

-
-
-

{disputeRate}%

-

Dispute rate

-
-
-

48h

-

Avg resolution time

-
-
-

62%

-

Released to seller

-
-
-

31%

-

Refunded to buyer

-
-
-
- ); -} - -// ── Page ─────────────────────────────────────────────────────────────────── - -export default function AdminAnalyticsPage() { - const [stats, setStats] = useState(null); - const [loading, setLoading] = useState(true); - const [range, setRange] = useState('30d'); - - useEffect(() => { - AdminService.getStats() - .then(setStats) - .finally(() => setLoading(false)); - }, []); - - if (loading) { - return ( -
- -
- ); - } - - if (!stats) return null; - - return ( -
- {/* Header + Range Selector */} -
-
-

Analytics

-

Platform-wide metrics and trends

-
-
- {RANGES.map((r) => ( - - ))} -
-
- - {/* Charts row */} -
- - -
- - {/* Dispute metrics + Top users */} -
- - -
-
- ); -} diff --git a/apps/frontend/app/admin/assets/page.tsx b/apps/frontend/app/admin/assets/page.tsx deleted file mode 100644 index 9966a866..00000000 --- a/apps/frontend/app/admin/assets/page.tsx +++ /dev/null @@ -1,336 +0,0 @@ -'use client'; - -import React, { useState, useEffect } from 'react'; -import { - Coins, - Plus, - Trash2, - ToggleLeft, - ToggleRight, - Shield, - Loader2, - Globe, - AlertTriangle, - Info, - CheckCircle, -} from 'lucide-react'; -import { AssetService, IAllowedAsset } from '@/services/assets'; -import { toast } from 'sonner'; - -export default function AdminAssetsPage() { - const [assets, setAssets] = useState([]); - const [loading, setLoading] = useState(true); - - // Form state - const [code, setCode] = useState(''); - const [issuer, setIssuer] = useState(''); - const [displayName, setDisplayName] = useState(''); - const [iconUrl, setIconUrl] = useState(''); - const [decimals, setDecimals] = useState(7); - const [showAddForm, setShowAddForm] = useState(false); - const [submitting, setSubmitting] = useState(false); - - useEffect(() => { - loadAssets(); - }, []); - - const loadAssets = async () => { - try { - setLoading(true); - const res = await AssetService.getAllAssets(); - setAssets(res); - } catch (e) { - console.error(e); - toast.error('Failed to load assets list'); - } finally { - setLoading(false); - } - }; - - const handleAddAsset = async (e: React.FormEvent) => { - e.preventDefault(); - if (!code.trim() || !displayName.trim()) { - toast.error('Asset code and display name are required'); - return; - } - - // Issuer length check for non-native assets - if (code !== 'XLM' && issuer.length !== 56) { - toast.error('Stellar issuer address must be exactly 56 characters'); - return; - } - - setSubmitting(true); - try { - await AssetService.createAsset({ - code: code.toUpperCase(), - issuer: code === 'XLM' ? undefined : issuer, - displayName, - iconUrl: iconUrl || undefined, - decimals, - active: true, - }); - - toast.success(`${code.toUpperCase()} added to whitelist successfully`); - setCode(''); - setIssuer(''); - setDisplayName(''); - setIconUrl(''); - setDecimals(7); - setShowAddForm(false); - loadAssets(); - } catch (error) { - console.error(error); - toast.error('Failed to add custom asset'); - } finally { - setSubmitting(false); - } - }; - - const handleToggleActive = async (asset: IAllowedAsset) => { - try { - const updated = await AssetService.updateAsset(asset.id, { - active: !asset.active, - }); - toast.success(`${asset.code} is now ${updated.active ? 'active' : 'inactive'}`); - loadAssets(); - } catch (error) { - console.error(error); - toast.error('Failed to update asset status'); - } - }; - - const handleDeleteAsset = async (id: string, code: string) => { - if (code === 'XLM' || code === 'USDC') { - toast.error('Default system assets cannot be deleted'); - return; - } - - if (!confirm(`Are you sure you want to remove ${code} from the whitelist?`)) { - return; - } - - try { - await AssetService.deleteAsset(id); - toast.success(`${code} removed successfully`); - loadAssets(); - } catch (error) { - console.error(error); - toast.error('Failed to remove asset'); - } - }; - - const truncateAddress = (addr?: string) => { - if (!addr) return 'Native'; - return `${addr.slice(0, 10)}...${addr.slice(-10)}`; - }; - - return ( -
- {/* Title */} -
-
-

- - Asset Whitelist Manager -

-

- Configure Stellar assets supported for smart contract escrows. -

-
- - -
- - {/* Add Custom Asset Form */} - {showAddForm && ( -
-

Register Custom Stellar Token

-
- -
- - setCode(e.target.value)} - placeholder="e.g. yXLM, RMT" - required - className="w-full bg-white/[0.02] border border-white/5 rounded-lg p-2.5 text-xs text-white placeholder-gray-600 focus:outline-none focus:border-purple-500/50" - /> -
- -
- - setDisplayName(e.target.value)} - placeholder="e.g. Yield-bearing XLM" - required - className="w-full bg-white/[0.02] border border-white/5 rounded-lg p-2.5 text-xs text-white placeholder-gray-600 focus:outline-none focus:border-purple-500/50" - /> -
- -
- - setIssuer(e.target.value)} - disabled={code.toUpperCase() === 'XLM'} - placeholder={code.toUpperCase() === 'XLM' ? 'Not required for native token' : 'e.g. GDRXE2BJUA...'} - required={code.toUpperCase() !== 'XLM'} - className="w-full bg-white/[0.02] border border-white/5 rounded-lg p-2.5 text-xs text-white placeholder-gray-600 focus:outline-none focus:border-purple-500/50 disabled:opacity-50" - /> -
- -
- - setIconUrl(e.target.value)} - placeholder="e.g. https://domain.com/token.png" - className="w-full bg-white/[0.02] border border-white/5 rounded-lg p-2.5 text-xs text-white placeholder-gray-600 focus:outline-none focus:border-purple-500/50" - /> -
- -
- - setDecimals(Number(e.target.value))} - min="0" - max="18" - className="w-full bg-white/[0.02] border border-white/5 rounded-lg p-2.5 text-xs text-white placeholder-gray-600 focus:outline-none focus:border-purple-500/50" - /> -
- -
- - -
- -
-
- )} - - {/* Assets List */} -
- {loading ? ( -
- -

Loading whitelisted assets...

-
- ) : ( -
- - - - - - - - - - - - {assets.map((asset) => ( - - - {/* Name & Icon */} - - - {/* Issuer Key */} - - - {/* Decimals */} - - - {/* Status */} - - - {/* Actions */} - - - - ))} - -
AssetIssuerDecimalsStatusActions
-
- {asset.iconUrl ? ( - {asset.code} - ) : ( - - )} -
-
- {asset.code} - {asset.displayName} -
-
- {asset.issuer ? ( - {truncateAddress(asset.issuer)} - ) : ( - - Native XLM - - )} - {asset.decimals} - - {asset.active ? 'Active' : 'Inactive'} - - - - - -
-
- )} -
- -
- ); -} diff --git a/apps/frontend/app/admin/audit-logs/page.tsx b/apps/frontend/app/admin/audit-logs/page.tsx deleted file mode 100644 index fda7308b..00000000 --- a/apps/frontend/app/admin/audit-logs/page.tsx +++ /dev/null @@ -1,292 +0,0 @@ -'use client'; - -import React, { useState, useEffect, useCallback } from 'react'; -import { - FileText, - Filter, - ChevronLeft, - ChevronRight, - Search, - Loader2, - Calendar, - User, - Activity, - Database, -} from 'lucide-react'; -import { AdminService } from '@/services/admin'; -import { IAuditLogResponse, IAuditLogFilters } from '@/types/admin'; - -const ACTION_COLORS: Record = { - SUSPEND_USER: 'text-red-400 bg-red-500/10', - CREATE_ESCROW: 'text-emerald-400 bg-emerald-500/10', - UPDATE_ESCROW: 'text-blue-400 bg-blue-500/10', - CONSISTENCY_CHECK: 'text-purple-400 bg-purple-500/10', - LOGIN: 'text-cyan-400 bg-cyan-500/10', - ROLE_CHANGE: 'text-yellow-400 bg-yellow-500/10', -}; - -const RESOURCE_ICONS: Record = { - USER: User, - ESCROW: Database, - SYSTEM: Activity, -}; - -export default function AdminAuditLogsPage() { - const [data, setData] = useState(null); - const [loading, setLoading] = useState(true); - const [page, setPage] = useState(1); - const [filters, setFilters] = useState({}); - const [showFilters, setShowFilters] = useState(false); - - const actionTypes = ['SUSPEND_USER', 'CREATE_ESCROW', 'UPDATE_ESCROW', 'CONSISTENCY_CHECK', 'LOGIN', 'ROLE_CHANGE']; - const resourceTypes = ['USER', 'ESCROW', 'SYSTEM']; - const pageSize = 15; - - const fetchLogs = useCallback(async () => { - setLoading(true); - try { - const result = await AdminService.getAuditLogs({ - ...filters, - page, - pageSize, - }); - setData(result); - } finally { - setLoading(false); - } - }, [page, filters]); - - useEffect(() => { - fetchLogs(); - }, [fetchLogs]); - - const totalPages = data ? Math.ceil(data.total / pageSize) : 1; - - const updateFilter = (key: keyof IAuditLogFilters, value: string) => { - setFilters(prev => ({ - ...prev, - [key]: value || undefined, - })); - setPage(1); - }; - - const clearFilters = () => { - setFilters({}); - setPage(1); - }; - - const activeFilterCount = Object.values(filters).filter(Boolean).length; - - return ( -
-
-

Audit Logs

-

- Track all administrative actions on the platform -

-
- - {/* Filter toggle */} -
- - {activeFilterCount > 0 && ( - - )} -
- - {/* Filter panel */} - {showFilters && ( -
- {/* Actor ID */} -
- -
- - updateFilter('actorId', e.target.value)} - className="w-full pl-9 pr-3 py-2 bg-white/[0.03] border border-white/5 rounded-lg text-sm text-white placeholder:text-gray-600 focus:outline-none focus:border-purple-500/50 transition-colors" - /> -
-
- - {/* Action Type */} -
- - -
- - {/* Resource Type */} -
- - -
- - {/* Date range */} -
- -
-
- - updateFilter('from', e.target.value)} - className="w-full pl-8 pr-2 py-2 bg-white/[0.03] border border-white/5 rounded-lg text-xs text-white focus:outline-none focus:border-purple-500/50 transition-colors" - /> -
-
- - updateFilter('to', e.target.value)} - className="w-full pl-8 pr-2 py-2 bg-white/[0.03] border border-white/5 rounded-lg text-xs text-white focus:outline-none focus:border-purple-500/50 transition-colors" - /> -
-
-
-
- )} - - {/* Log table */} -
- {loading ? ( -
- -
- ) : ( - <> -
- - - - - - - - - - - - {data?.data.map((log) => { - const ResourceIcon = RESOURCE_ICONS[log.resourceType] || Activity; - const actionColor = ACTION_COLORS[log.actionType] || 'text-gray-400 bg-gray-500/10'; - return ( - - - - - - - - ); - })} - {data?.data.length === 0 && ( - - - - )} - -
TimestampActorActionResourceResource ID
-

- {new Date(log.createdAt).toLocaleDateString()} -

-

- {new Date(log.createdAt).toLocaleTimeString()} -

-
- {log.actorId} - - - {log.actionType.replace(/_/g, ' ')} - - - - - {log.resourceType} - - - - {log.resourceId || '—'} - -
- -

No audit logs found

-

Try adjusting your filters

-
-
- - {/* Pagination */} - {totalPages > 1 && ( -
-

- Page {page} of {totalPages} ({data?.total} logs) -

-
- - -
-
- )} - - )} -
-
- ); -} diff --git a/apps/frontend/app/admin/disputes/page.tsx b/apps/frontend/app/admin/disputes/page.tsx deleted file mode 100644 index 6cd31fec..00000000 --- a/apps/frontend/app/admin/disputes/page.tsx +++ /dev/null @@ -1,622 +0,0 @@ -'use client'; - -import React, { useState, useEffect, useRef } from 'react'; -import { - AlertTriangle, - Clock, - Coins, - MessageSquare, - History, - User, - CheckCircle2, - XCircle, - ArrowLeft, - Check, - Loader2, - Scale, - FileText, - ChevronRight, - Shield, - FileCode, -} from 'lucide-react'; -import { AdminService } from '@/services/admin'; -import { IAdminDispute } from '@/types/admin'; -import { toast } from 'sonner'; - -export default function AdminDisputesPage() { - const [disputes, setDisputes] = useState([]); - const [selectedDispute, setSelectedDispute] = useState(null); - const [loading, setLoading] = useState(true); - const [sortBy, setSortBy] = useState<'oldest' | 'newest' | 'most_evidence' | 'highest_amount'>('oldest'); - - // Resolution form states - const [outcome, setOutcome] = useState<'released_to_seller' | 'refunded_to_buyer' | 'split'>('released_to_seller'); - const [resolutionNotes, setResolutionNotes] = useState(''); - const [sellerPercent, setSellerPercent] = useState(50); - const [buyerPercent, setBuyerPercent] = useState(50); - - // Confirmation dialog state - const [showConfirmModal, setShowConfirmModal] = useState(false); - const [submitting, setSubmitting] = useState(false); - - const notesInputRef = useRef(null); - - // Fetch disputes - useEffect(() => { - loadDisputes(); - }, [sortBy]); - - const loadDisputes = async () => { - try { - setLoading(true); - const res = await AdminService.getDisputes({ status: 'open', sortBy }); - setDisputes(res.disputes); - - // Auto-select first dispute if none selected and on desktop - if (res.disputes.length > 0 && !selectedDispute) { - setSelectedDispute(res.disputes[0]); - } else if (selectedDispute) { - // Refresh selected dispute details - const updatedSelected = res.disputes.find(d => d.id === selectedDispute.id); - setSelectedDispute(updatedSelected || res.disputes[0] || null); - } - } catch (error) { - console.error('Error fetching disputes:', error); - toast.error('Failed to load disputes'); - } finally { - setLoading(false); - } - }; - - // Keyboard shortcuts - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - // Don't trigger shortcuts when user is typing - if ( - document.activeElement?.tagName === 'INPUT' || - document.activeElement?.tagName === 'TEXTAREA' || - document.activeElement?.getAttribute('contenteditable') === 'true' - ) { - return; - } - - if (e.key === 'r' || e.key === 'R') { - e.preventDefault(); - notesInputRef.current?.focus(); - toast.info('Form focused (Shortcut R)'); - } else if (e.key === 'b' || e.key === 'B') { - e.preventDefault(); - setOutcome('refunded_to_buyer'); - toast.info('Outcome set to Refund Buyer (Shortcut B)'); - } else if (e.key === 's' || e.key === 'S') { - e.preventDefault(); - setOutcome('released_to_seller'); - toast.info('Outcome set to Pay Seller (Shortcut S)'); - } - }; - - window.addEventListener('keydown', handleKeyDown); - return () => window.removeEventListener('keydown', handleKeyDown); - }, [selectedDispute]); - - const getPriority = (amountStr: string, createdAt: string) => { - const amount = parseFloat(amountStr); - const ageDays = (Date.now() - new Date(createdAt).getTime()) / (24 * 60 * 60 * 1000); - if (amount >= 10000 || ageDays >= 7) { - return { label: 'High', color: 'text-red-400 bg-red-500/10 border-red-500/20' }; - } - if (amount >= 1000 || ageDays >= 3) { - return { label: 'Medium', color: 'text-amber-400 bg-amber-500/10 border-amber-500/20' }; - } - return { label: 'Low', color: 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20' }; - }; - - const handleOpenConfirm = (e: React.FormEvent) => { - e.preventDefault(); - if (!resolutionNotes.trim()) { - toast.error('Resolution notes are required'); - return; - } - if (outcome === 'split' && sellerPercent + buyerPercent !== 100) { - toast.error('Split percentages must sum to 100%'); - return; - } - setShowConfirmModal(true); - }; - - const handleSubmitResolution = async () => { - if (!selectedDispute) return; - setSubmitting(true); - try { - await AdminService.resolveDispute(selectedDispute.id, { - outcome, - resolutionNotes, - sellerPercent: outcome === 'split' ? sellerPercent : undefined, - buyerPercent: outcome === 'split' ? buyerPercent : undefined, - }); - - toast.success('Dispute resolved successfully'); - setResolutionNotes(''); - setShowConfirmModal(false); - - // Reload - const res = await AdminService.getDisputes({ status: 'open', sortBy }); - setDisputes(res.disputes); - if (res.disputes.length > 0) { - setSelectedDispute(res.disputes[0]); - } else { - setSelectedDispute(null); - } - } catch (error) { - console.error('Error resolving dispute:', error); - toast.error('Failed to resolve dispute'); - } finally { - setSubmitting(false); - } - }; - - const handleSplitChange = (val: number, isSeller: boolean) => { - if (isSeller) { - setSellerPercent(val); - setBuyerPercent(100 - val); - } else { - setBuyerPercent(val); - setSellerPercent(100 - val); - } - }; - - return ( -
- {/* Header */} -
-
-

- - Dispute Resolution -

-

- Review evidence, communicate log, and resolve conflicts between parties. -

-
- - {/* Sort Controls */} -
- Sort By: - -
-
- - {loading && disputes.length === 0 ? ( -
-
- -

Loading open disputes...

-
-
- ) : disputes.length === 0 ? ( -
- -

No Open Disputes

-

- All disputes have been successfully resolved. Great job! -

-
- ) : ( - /* Split view grid */ -
- - {/* LEFT LIST: visible or hidden on mobile depending on selected state */} -
-

- Open Cases ({disputes.length}) -

-
- {disputes.map((dispute) => { - const priority = getPriority(dispute.escrow.amount, dispute.createdAt); - const isSelected = selectedDispute?.id === dispute.id; - return ( - - ); - })} -
-
- - {/* RIGHT DETAIL SPLIT: visible on desktop, mobile list-to-detail toggle */} - {selectedDispute && ( -
- - {/* Back button for mobile detail view */} -
- -
- - {/* Detail view left (Escrow Info + Evidence + Timeline + Chat) */} -
- - {/* Real-time Escrow details card */} -
-
- Escrow Contract Details - DISPUTED -
- -
-

{selectedDispute.escrow.title}

-

- {selectedDispute.escrow.description} -

-
- -
-
-

Amount

-

- {selectedDispute.escrow.amount} {selectedDispute.escrow.asset} -

-
-
-

Dispute Raised By

-

- {selectedDispute.filedBy.walletAddress} -

-
-
- - {/* Reason & Evidence */} -
- Reason for Dispute -

- {selectedDispute.reason} -

-
- - {selectedDispute.evidence && selectedDispute.evidence.length > 0 && ( -
- Evidence Documents -
- {selectedDispute.evidence.map((ev, idx) => { - const isImage = ev.match(/\.(jpg|jpeg|png|webp)(\?.*)?$/i); - return ( -
- {isImage ? ( -
- {/* eslint-disable-next-line @next/next/no-img-element */} - {`Evidence -
- -
-
- ) : ( -
-
- - {ev} -
-
- )} -
- -
-
- ); - })} -
-
- )} -
- - {/* Communication Log */} -
-

- - Communication Log -

- - {selectedDispute.communicationLog && selectedDispute.communicationLog.length > 0 ? ( -
- {selectedDispute.communicationLog.map((chat) => ( -
-
- {chat.sender} - - {new Date(chat.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - -
-

{chat.message}

-
- ))} -
- ) : ( -

No communication logs recorded.

- )} -
- - {/* Resolution history for re-opened disputes */} - {selectedDispute.resolutionHistory && selectedDispute.resolutionHistory.length > 0 && ( -
-

- - Resolution History (Re-opened Case) -

-
- {selectedDispute.resolutionHistory.map((hist, idx) => ( -
-
- - Outcome: {hist.outcome.replace(/_/g, ' ')} - - - {new Date(hist.resolvedAt).toLocaleDateString()} - -
-

- {hist.notes} -

-

- Arbitrator: {hist.resolvedBy} -

-
- ))} -
-
- )} - - {/* Timeline */} -
-

Dispute Timeline

-
- {selectedDispute.timeline?.map((item) => ( -
-
-
-
-
-
-
- {item.title} - - {new Date(item.timestamp).toLocaleDateString()} - -
-

{item.description}

- Actor: {item.actor} -
-
- ))} -
-
- -
- - {/* Detail view right (Resolution form) */} -
-
-
-

Arbitration Resolution

-

- Keyboard shortcuts: R=Focus notes, B=Buyer wins, S=Seller wins -

-
- -
- {/* Outcome Type */} -
- -
- - - -
-
- - {/* Custom Split Sliders */} - {outcome === 'split' && ( -
-
-
- Buyer Receives - {buyerPercent}% -
- handleSplitChange(Number(e.target.value), false)} - className="w-full h-1 bg-white/10 rounded-lg appearance-none cursor-pointer accent-purple-500" - /> -
- -
-
- Seller Receives - {sellerPercent}% -
- handleSplitChange(Number(e.target.value), true)} - className="w-full h-1 bg-white/10 rounded-lg appearance-none cursor-pointer accent-purple-500" - /> -
-
- )} - - {/* Notes */} -
- -