diff --git a/.github/instructions/mcp-server.instructions.md b/.github/instructions/mcp-server.instructions.md index 2312d7d..8967a78 100644 --- a/.github/instructions/mcp-server.instructions.md +++ b/.github/instructions/mcp-server.instructions.md @@ -145,7 +145,7 @@ Implement consistent difficulty levels across all games: ```typescript export function calculateTicTacToeMove( gameState: TicTacToeGameState, - difficulty: 'easy' | 'medium' | 'hard' = 'medium' + difficulty: Difficulty = 'medium' ): TicTacToeMove { const validMoves = getValidMoves(gameState) diff --git a/.github/instructions/shared-library.instructions.md b/.github/instructions/shared-library.instructions.md index 7e09f18..37ce628 100644 --- a/.github/instructions/shared-library.instructions.md +++ b/.github/instructions/shared-library.instructions.md @@ -140,6 +140,89 @@ export function getGameDisplayName(type: GameType): string { } ``` +## Constants and Common Values + +### Centralized Constants and Derived Types +**Types are derived from constants using `as const` assertions - constants are the single source of truth:** + +```typescript +// ✅ Constants define the source of truth +export const DIFFICULTIES = ['easy', 'medium', 'hard'] as const +export const GAME_TYPES = ['tic-tac-toe', 'rock-paper-scissors'] as const +export const PLAYER_IDS = { HUMAN: 'player1', PLAYER2: 'player2', AI: 'ai' } as const + +// ✅ Types are derived from constants +export type Difficulty = typeof DIFFICULTIES[number] +export type GameType = typeof GAME_TYPES[number] +export type PlayerId = typeof PLAYER_IDS[keyof typeof PLAYER_IDS] + +// ✅ Import the derived types +import type { Difficulty, GameType } from '@turn-based-mcp/shared' + +// ❌ Don't define duplicate union types +export type Difficulty = 'easy' | 'medium' | 'hard' // This duplicates the constants! +``` + +### Available Constants +Key constants provided by the shared library: + +```typescript +// Constants with derived types +export const GAME_TYPES = ['tic-tac-toe', 'rock-paper-scissors'] as const +export const DIFFICULTIES = ['easy', 'medium', 'hard'] as const +export const PLAYER_IDS = { HUMAN: 'player1', PLAYER2: 'player2', AI: 'ai' } as const +export const GAME_STATUSES = ['waiting', 'playing', 'finished'] as const + +// Derived types (auto-generated from constants) +export type GameType = typeof GAME_TYPES[number] +export type Difficulty = typeof DIFFICULTIES[number] +export type PlayerId = typeof PLAYER_IDS[keyof typeof PLAYER_IDS] +export type GameStatus = typeof GAME_STATUSES[number] + +// Default values +export const DEFAULT_PLAYER_NAME = 'Player' +export const DEFAULT_AI_DIFFICULTY: Difficulty = 'medium' + +// UI display configuration +export const DIFFICULTY_DISPLAY = { + easy: { emoji: '😌', label: 'Easy' }, + medium: { emoji: '🎯', label: 'Medium' }, + hard: { emoji: '🔥', label: 'Hard' } +} as const +``` + +### Type Guards and Utilities +Use provided validation functions that work with the constants: + +```typescript +// Type guards (check against the constant arrays) +export function isSupportedGameType(gameType: string): gameType is GameType +export function isValidDifficulty(difficulty: string): difficulty is Difficulty +export function isValidPlayerId(playerId: string): playerId is PlayerId + +// Display helpers +export function getDifficultyDisplay(difficulty: Difficulty) +``` + +### Architecture Benefits +This approach ensures: +- **Single source of truth**: Constants define what values are valid +- **Type safety**: TypeScript derives exact types from the constant values +- **Runtime validation**: Type guards check against the same arrays used to derive types +- **Maintainability**: Add a new difficulty by updating one constant array + +### Testing Constants +For mocking and test data, use shared testing utilities: + +```typescript +// Test data from shared/src/testing/ +import { + mockTicTacToeGameState, + mockRPSGameState, + createMockGameSession +} from '@turn-based-mcp/shared/testing' +``` + ## Testing Infrastructure ### Test Database Utilities diff --git a/.github/instructions/testing.instructions.md b/.github/instructions/testing.instructions.md index c15e74e..f51a845 100644 --- a/.github/instructions/testing.instructions.md +++ b/.github/instructions/testing.instructions.md @@ -31,6 +31,33 @@ Follow these testing patterns for the turn-based games platform: }) ``` +## Shared Testing Utilities + +**Use centralized mock data and test utilities from the shared package:** + +```typescript +// ✅ Import shared testing utilities +import { + mockTicTacToeGameState, + mockRPSGameState, + createMockGameSession, + setupTestDatabase, + clearTestDatabase +} from '@turn-based-mcp/shared/testing' + +// ✅ Use shared constants in tests +import { DIFFICULTIES, GAME_TYPES } from '@turn-based-mcp/shared' + +// ❌ Don't recreate mock data locally +const localMockGameState = { /* duplicated data */ } // Use shared mocks instead! +``` + +**Available shared testing utilities:** +- Mock game states: `mockTicTacToeGameState`, `mockRPSGameState` +- Factory functions: `createMockGameSession`, `createMockPlayer` +- Database utilities: `setupTestDatabase`, `clearTestDatabase`, `teardownTestDatabase` +- Type assertions and validation helpers + ## Component Testing - Always render components with realistic props diff --git a/.github/instructions/typescript.instructions.md b/.github/instructions/typescript.instructions.md index 39bba63..cbc2b29 100644 --- a/.github/instructions/typescript.instructions.md +++ b/.github/instructions/typescript.instructions.md @@ -15,6 +15,37 @@ Follow these TypeScript patterns for consistent, type-safe code: - Use barrel exports (`index.ts`) for clean imports - Re-export shared types from `@turn-based-mcp/shared` +### Shared Types and Constants +**Always import types derived from shared constants - don't duplicate union types:** + +```typescript +// ✅ Import types derived from constants +import type { Difficulty, GameType, PlayerId } from '@turn-based-mcp/shared' +import { DIFFICULTIES, DEFAULT_AI_DIFFICULTY, GAME_TYPES, PLAYER_IDS } from '@turn-based-mcp/shared' + +// ✅ Use the imported types +const [aiDifficulty, setAiDifficulty] = useState('medium') +const playerIds: PlayerId[] = Object.values(PLAYER_IDS) + +// ❌ Don't define duplicate union types +type Difficulty = 'easy' | 'medium' | 'hard' // This duplicates shared constants! +type PlayerId = 'player1' | 'player2' | 'ai' // Use the derived type instead! +``` + +**Key principle: Types are derived from constants using `as const` assertions:** +```typescript +// In shared/src/constants/game-constants.ts +export const DIFFICULTIES = ['easy', 'medium', 'hard'] as const +export type Difficulty = typeof DIFFICULTIES[number] // 'easy' | 'medium' | 'hard' +``` + +**Common types available from shared package:** +- `Difficulty` - AI difficulty levels (derived from `DIFFICULTIES`) +- `GameType` - Supported game types (derived from `GAME_TYPES`) +- `PlayerId` - Player identifiers (derived from `PLAYER_IDS`) +- `GameStatus` - Game state values (derived from `GAME_STATUSES`) +- Game-specific interfaces: `TicTacToeGameState`, `RPSGameState`, etc. + ### Interface Design - Use interfaces for object shapes and component props - Include JSDoc comments for complex properties diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75a5c72..2854e90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,26 +31,30 @@ jobs: run: npm ci - name: Build shared package - run: npm run build --workspace=shared + run: npm run build --workspace=@turn-based-mcp/shared + + - name: Verify shared build artifacts + run: | + test -f shared/dist/index.js || (echo 'shared/dist/index.js missing' && exit 1) + test -f shared/dist/testing/index.js || (echo 'shared/dist/testing/index.js missing' && exit 1) - name: Type check all workspaces run: npm run type-check - name: Build web package - run: npm run build --workspace=web + run: npm run build --workspace=@turn-based-mcp/web - name: Build mcp-server package - run: npm run build --workspace=mcp-server - - - name: Lint web package - run: npm run lint --workspace=web - - # Future: Add test steps when tests are implemented - # - name: Run tests - shared - # run: npm run test --workspace=shared - # - # - name: Run tests - web - # run: npm run test --workspace=web - # - # - name: Run tests - mcp-server - # run: npm run test --workspace=mcp-server \ No newline at end of file + run: npm run build --workspace=@turn-based-mcp/mcp-server + + - name: Run tests - shared + run: npm run test --workspace=@turn-based-mcp/shared + + - name: Run tests - web + run: npm run test --workspace=@turn-based-mcp/web + + - name: Run tests - mcp-server + run: npm run test --workspace=@turn-based-mcp/mcp-server + + - name: Lint all packages + run: npm run lint:all \ No newline at end of file diff --git a/.vscode/mcp.json b/.vscode/mcp.json index 4e34f72..3f00ddb 100644 --- a/.vscode/mcp.json +++ b/.vscode/mcp.json @@ -2,7 +2,7 @@ "servers": { "turn-based-games": { "command": "node", - "args": ["dist/index.js"], + "args": ["dist/server.js"], "cwd": "./mcp-server" }, "playwright": { diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..9dd4ba2 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,31 @@ +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.{js,mjs,cjs,ts}'], + languageOptions: { + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + }, + rules: { + // Add any global rules here + }, + }, + { + ignores: [ + '**/node_modules/**', + '**/dist/**', + '**/.next/**', + '**/coverage/**', + '**/*.config.js', + '**/*.config.ts', + ], + } +); diff --git a/mcp-server/eslint.config.js b/mcp-server/eslint.config.js new file mode 100644 index 0000000..24035a1 --- /dev/null +++ b/mcp-server/eslint.config.js @@ -0,0 +1,41 @@ +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.{js,mjs,cjs,ts}'], + languageOptions: { + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + }, + rules: { + // MCP server specific rules + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + '@typescript-eslint/explicit-function-return-type': 'warn', + '@typescript-eslint/no-explicit-any': 'warn', + 'no-console': 'off', // Console logging is often needed for MCP servers + }, + }, + { + files: ['**/*.test.ts', 'vitest.*.ts'], + rules: { + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, + }, + { + ignores: [ + 'node_modules/**', + 'dist/**', + 'coverage/**', + '*.config.js', + '*.config.ts', + ], + } +); diff --git a/mcp-server/jest.config.cjs b/mcp-server/jest.config.cjs deleted file mode 100644 index 71e25bf..0000000 --- a/mcp-server/jest.config.cjs +++ /dev/null @@ -1,39 +0,0 @@ -/** @type {import('jest').Config} */ -const config = { - preset: 'ts-jest', - testEnvironment: 'node', - setupFilesAfterEnv: ['/jest.setup.js'], - roots: ['/src'], - testMatch: [ - '**/__tests__/**/*.+(ts|tsx|js)', - '**/*.(test|spec).+(ts|tsx|js)' - ], - transform: { - '^.+\\.(ts|tsx)$': ['ts-jest', { - useESM: true - }] - }, - collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', - '!src/**/*.d.ts', - '!src/**/index.ts' - ], - coverageDirectory: 'coverage', - coverageReporters: ['text', 'lcov', 'html'], - coverageThreshold: { - global: { - branches: 85, - functions: 85, - lines: 85, - statements: 85 - } - }, - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - extensionsToTreatAsEsm: ['.ts'], - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', - '^@turn-based-mcp/shared$': '/../shared/src/index.ts' - } -}; - -module.exports = config; \ No newline at end of file diff --git a/mcp-server/jest.setup.js b/mcp-server/jest.setup.js deleted file mode 100644 index d6bcc59..0000000 --- a/mcp-server/jest.setup.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Jest setup for mcp-server package tests - * - * Sets up test database before all tests and cleans up after - */ - -const { setupTestDatabase, teardownTestDatabase } = require('@turn-based-mcp/shared') - -// Setup test database before all tests -beforeAll(async () => { - try { - await setupTestDatabase(true) // Use in-memory database for speed - } catch (error) { - console.error('Failed to setup test database:', error) - throw error - } -}) - -// Cleanup test database after all tests -afterAll(async () => { - try { - await teardownTestDatabase() - } catch (error) { - console.error('Failed to teardown test database:', error) - // Don't throw here to avoid masking other test failures - } -}) diff --git a/mcp-server/package.json b/mcp-server/package.json index 88e9df2..cdb5ef9 100644 --- a/mcp-server/package.json +++ b/mcp-server/package.json @@ -2,27 +2,31 @@ "name": "@turn-based-mcp/mcp-server", "version": "1.0.0", "description": "MCP server for turn-based games AI opponent", - "main": "dist/index.js", + "main": "dist/server.js", "type": "module", "scripts": { "build": "tsc", "dev": "tsc --watch", - "start": "node dist/index.js", + "start": "node dist/server.js", "clean": "rm -rf dist", "type-check": "tsc --noEmit", - "test": "jest", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage" + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "test:ui": "vitest --ui", + "lint": "eslint .", + "lint:fix": "eslint . --fix" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.17.0", + "@modelcontextprotocol/sdk": "^1.17.1", "@turn-based-mcp/shared": "file:../shared" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/node": "^24.1.0", - "jest": "^30.0.5", - "ts-jest": "^29.4.0", - "typescript": "^5.5.0" + "@eslint/js": "^9.32.0", + "@types/node": "^24.2.0", + "@vitest/ui": "^3.2.4", + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0", + "vitest": "^3.2.4" } } diff --git a/mcp-server/src/ai/rock-paper-scissors-ai.test.ts b/mcp-server/src/ai/rock-paper-scissors-ai.test.ts index 6a566f8..830ba91 100644 --- a/mcp-server/src/ai/rock-paper-scissors-ai.test.ts +++ b/mcp-server/src/ai/rock-paper-scissors-ai.test.ts @@ -1,4 +1,4 @@ -import { RockPaperScissorsAI, type Strategy, type Difficulty } from './rock-paper-scissors-ai.js'; +import { RockPaperScissorsAI } from './rock-paper-scissors-ai.js'; import type { RPSGameState, RPSChoice, Player } from '@turn-based-mcp/shared'; import { RockPaperScissorsGame } from '@turn-based-mcp/shared'; @@ -59,8 +59,8 @@ describe('RockPaperScissorsAI', () => { }); it('should counter the most frequent opponent choice', async () => { - // Create a state where opponent played rock 2 times, paper 1 time - let state = initialState; + // Create a state where opponent played rock 2 times, paper 1 time + let state = initialState; state = game.applyMove(state, { choice: 'rock' }, 'player1'); state = game.applyMove(state, { choice: 'paper' }, 'ai'); state = game.applyMove(state, { choice: 'rock' }, 'player1'); @@ -74,8 +74,8 @@ describe('RockPaperScissorsAI', () => { }); it('should handle tied frequencies', async () => { - // Create equal frequency scenario - let state = initialState; + // Create equal frequency scenario + let state = initialState; state = game.applyMove(state, { choice: 'rock' }, 'player1'); state = game.applyMove(state, { choice: 'paper' }, 'ai'); state = game.applyMove(state, { choice: 'paper' }, 'player1'); @@ -126,7 +126,7 @@ describe('RockPaperScissorsAI', () => { }); it('should default to random for invalid difficulty', async () => { - const choice = await ai.makeChoice(initialState, 'invalid' as Difficulty); + const choice = await ai.makeChoice(initialState, 'invalid' as any); expect(['rock', 'paper', 'scissors']).toContain(choice); }); }); @@ -156,7 +156,7 @@ describe('RockPaperScissorsAI', () => { }); describe('analyzeGameState', () => { - it('should provide basic game analysis for initial state', () => { + it('should provide basic game analysis for initial state', () => { const analysis = ai.analyzeGameState(initialState); expect(analysis).toContain('Game Status: playing'); @@ -165,7 +165,7 @@ describe('RockPaperScissorsAI', () => { }); it('should show round history after moves', async () => { - let state = initialState; + let state = initialState; state = game.applyMove(state, { choice: 'rock' }, 'player1'); state = game.applyMove(state, { choice: 'paper' }, 'ai'); @@ -175,7 +175,7 @@ describe('RockPaperScissorsAI', () => { }); it('should analyze opponent patterns', async () => { - let state = initialState; + let state = initialState; // Create pattern: rock, paper, rock state = game.applyMove(state, { choice: 'rock' }, 'player1'); state = game.applyMove(state, { choice: 'scissors' }, 'ai'); @@ -196,7 +196,7 @@ describe('RockPaperScissorsAI', () => { }); it('should show current score correctly', async () => { - let state = initialState; + let state = initialState; // reassigned in subsequent applyMove calls // Player 1 wins first round state = game.applyMove(state, { choice: 'rock' }, 'player1'); state = game.applyMove(state, { choice: 'scissors' }, 'ai'); @@ -208,7 +208,7 @@ describe('RockPaperScissorsAI', () => { }); it('should handle finished games', () => { - let state = initialState; + const state = { ...initialState }; // mutate copy, no reassignment afterward state.status = 'finished'; state.winner = 'player1'; state.currentRound = 3; @@ -218,7 +218,7 @@ describe('RockPaperScissorsAI', () => { }); it('should handle draw rounds', async () => { - let state = initialState; + let state = initialState; // reassigned in applyMove calls state = game.applyMove(state, { choice: 'rock' }, 'player1'); state = game.applyMove(state, { choice: 'rock' }, 'player2'); @@ -278,7 +278,7 @@ describe('RockPaperScissorsAI', () => { }); it('should maintain consistent difficulty behavior', async () => { - const difficulties: Difficulty[] = ['easy', 'medium', 'hard']; + const difficulties: any[] = ['easy', 'medium', 'hard']; for (const difficulty of difficulties) { const choice = await ai.makeChoice(initialState, difficulty); @@ -287,7 +287,7 @@ describe('RockPaperScissorsAI', () => { }); it('should handle incomplete rounds', () => { - let state = initialState; + const state = initialState; // Only player1 has made a choice in current round state.rounds[0] = { player1Choice: 'rock' }; @@ -307,7 +307,7 @@ describe('RockPaperScissorsAI', () => { it('should be consistent with same game state', async () => { // Create a deterministic scenario - let state = initialState; + let state = initialState; state = game.applyMove(state, { choice: 'rock' }, 'player1'); state = game.applyMove(state, { choice: 'paper' }, 'player2'); state = game.applyMove(state, { choice: 'rock' }, 'player1'); diff --git a/mcp-server/src/ai/rock-paper-scissors-ai.ts b/mcp-server/src/ai/rock-paper-scissors-ai.ts index 6b11056..c6a2b1d 100644 --- a/mcp-server/src/ai/rock-paper-scissors-ai.ts +++ b/mcp-server/src/ai/rock-paper-scissors-ai.ts @@ -1,8 +1,7 @@ -import type { RPSGameState, RPSMove, RPSChoice } from '@turn-based-mcp/shared' +import type { RPSGameState, RPSChoice, Difficulty } from '@turn-based-mcp/shared' import { RockPaperScissorsGame } from '@turn-based-mcp/shared' export type Strategy = 'random' | 'adaptive' | 'pattern' -export type Difficulty = 'easy' | 'medium' | 'hard' /** * AI opponent for Rock Paper Scissors with multiple strategic approaches @@ -196,7 +195,7 @@ export class RockPaperScissorsAI { * Rebuilds the opponent history array from the game state's completed rounds. * Assumes player1 is the human opponent whose patterns we want to learn. */ - private updateOpponentHistory(gameState: RPSGameState) { + private updateOpponentHistory(gameState: RPSGameState): void { // Extract opponent moves from completed rounds this.opponentHistory = [] diff --git a/mcp-server/src/ai/rock-paper-scissors-security.test.ts b/mcp-server/src/ai/rock-paper-scissors-security.test.ts index ab32009..e819326 100644 --- a/mcp-server/src/ai/rock-paper-scissors-security.test.ts +++ b/mcp-server/src/ai/rock-paper-scissors-security.test.ts @@ -1,10 +1,8 @@ import { RockPaperScissorsAI } from './rock-paper-scissors-ai' -import { RockPaperScissorsGame } from '@turn-based-mcp/shared' import type { RPSGameState, RPSChoice } from '@turn-based-mcp/shared' describe('RockPaperScissorsAI - Current Move Security', () => { let ai: RockPaperScissorsAI - let game: RockPaperScissorsGame const createMockGameState = ( rounds: Array<{ @@ -31,7 +29,7 @@ describe('RockPaperScissorsAI - Current Move Security', () => { beforeEach(() => { ai = new RockPaperScissorsAI() - game = new RockPaperScissorsGame() + // separate game instance not required for these security-focused tests }) describe('Current Round Security Tests', () => { diff --git a/mcp-server/src/ai/tic-tac-toe-ai.test.ts b/mcp-server/src/ai/tic-tac-toe-ai.test.ts index 65e898a..24a555a 100644 --- a/mcp-server/src/ai/tic-tac-toe-ai.test.ts +++ b/mcp-server/src/ai/tic-tac-toe-ai.test.ts @@ -1,4 +1,4 @@ -import { TicTacToeAI, type Difficulty } from './tic-tac-toe-ai.js'; +import { TicTacToeAI } from './tic-tac-toe-ai.js'; import type { TicTacToeGameState, Player } from '@turn-based-mcp/shared'; import { TicTacToeGame } from '@turn-based-mcp/shared'; @@ -35,7 +35,7 @@ describe('TicTacToeAI', () => { it('should choose from available moves only', async () => { // Fill most of the board - let state = initialState; + const state = initialState; state.board = [ ['X', 'O', 'X'], ['O', 'X', 'O'], @@ -61,7 +61,7 @@ describe('TicTacToeAI', () => { describe('medium difficulty', () => { it('should win when possible', async () => { - let state = initialState; + const state = initialState; // Set up a winning scenario for AI state.board = [ ['O', 'O', null], @@ -74,7 +74,7 @@ describe('TicTacToeAI', () => { }); it('should block opponent from winning', async () => { - let state = initialState; + const state = initialState; // Set up scenario where player can win state.board = [ ['X', 'X', null], @@ -101,7 +101,7 @@ describe('TicTacToeAI', () => { }); it('should prefer center when available', async () => { - let state = initialState; + const state = initialState; state.board = [ ['X', null, null], [null, null, null], @@ -113,7 +113,7 @@ describe('TicTacToeAI', () => { }); it('should choose corners when center is taken', async () => { - let state = initialState; + const state = initialState; state.board = [ [null, null, null], [null, 'X', null], @@ -130,7 +130,7 @@ describe('TicTacToeAI', () => { }); it('should prioritize winning over blocking', async () => { - let state = initialState; + const state = initialState; // Both AI and player can win state.board = [ ['O', 'O', null], // AI can win here @@ -145,7 +145,7 @@ describe('TicTacToeAI', () => { describe('hard difficulty', () => { it('should play optimally using minimax', async () => { - let state = initialState; + const state = initialState; // Classic opening: AI should respond optimally to corner play state.board = [ ['X', null, null], @@ -158,7 +158,7 @@ describe('TicTacToeAI', () => { }); it('should never lose from a winning position', async () => { - let state = initialState; + const state = initialState; // AI is in a winning position state.board = [ ['O', 'X', null], @@ -171,7 +171,7 @@ describe('TicTacToeAI', () => { }); it('should force a draw from losing position', async () => { - let state = initialState; + const state = initialState; // Player has advantage but AI should force draw state.board = [ ['X', null, null], @@ -206,7 +206,7 @@ describe('TicTacToeAI', () => { }); it('should default to easy difficulty for invalid difficulty', async () => { - const move = await ai.makeMove(initialState, 'invalid' as Difficulty); + const move = await ai.makeMove(initialState, 'invalid' as any); expect(game.validateMove(initialState, move, 'ai')).toBe(true); }); }); @@ -223,7 +223,7 @@ describe('TicTacToeAI', () => { }); it('should detect winning opportunities', () => { - let state = initialState; + const state = initialState; state.board = [ ['O', 'O', null], ['X', null, null], @@ -235,7 +235,7 @@ describe('TicTacToeAI', () => { }); it('should detect player threats', () => { - let state = initialState; + const state = initialState; state.board = [ ['X', 'X', null], ['O', null, null], @@ -250,7 +250,7 @@ describe('TicTacToeAI', () => { }); it('should analyze board state correctly', () => { - let state = initialState; + const state = initialState; state.board = [ ['X', null, 'O'], [null, 'X', null], // Center has X @@ -264,7 +264,7 @@ describe('TicTacToeAI', () => { }); it('should handle game-ending scenarios', () => { - let state = initialState; + const state = initialState; state.board = [ ['X', 'X', 'X'], ['O', 'O', null], @@ -280,7 +280,7 @@ describe('TicTacToeAI', () => { describe('edge cases and robustness', () => { it('should handle full board gracefully', async () => { - let state = initialState; + const state = initialState; state.board = [ ['X', 'O', 'X'], ['O', 'X', 'O'], @@ -304,7 +304,7 @@ describe('TicTacToeAI', () => { }); it('should maintain consistent behavior across difficulties', async () => { - const difficulties: Difficulty[] = ['easy', 'medium', 'hard']; + const difficulties: any[] = ['easy', 'medium', 'hard']; for (const difficulty of difficulties) { const move = await ai.makeMove(initialState, difficulty); @@ -313,7 +313,7 @@ describe('TicTacToeAI', () => { }); it('should handle near-end game scenarios', async () => { - let state = initialState; + const state = initialState; // Only one move left state.board = [ ['X', 'O', 'X'], diff --git a/mcp-server/src/ai/tic-tac-toe-ai.ts b/mcp-server/src/ai/tic-tac-toe-ai.ts index 2bf8fa7..78c4493 100644 --- a/mcp-server/src/ai/tic-tac-toe-ai.ts +++ b/mcp-server/src/ai/tic-tac-toe-ai.ts @@ -1,8 +1,6 @@ -import type { TicTacToeGameState, TicTacToeMove } from '@turn-based-mcp/shared' +import type { TicTacToeGameState, TicTacToeMove, Difficulty, PlayerId } from '@turn-based-mcp/shared' import { TicTacToeGame } from '@turn-based-mcp/shared' -export type Difficulty = 'easy' | 'medium' | 'hard' - /** * AI opponent for Tic-Tac-Toe with configurable difficulty levels * @@ -195,11 +193,11 @@ export class TicTacToeAI { * Tests each valid move to see if it results in an immediate win. * Used for both finding AI wins and blocking opponent wins. */ - private findWinningMove(gameState: TicTacToeGameState, playerId: string): TicTacToeMove | null { - const validMoves = this.game.getValidMoves(gameState, playerId as any) + private findWinningMove(gameState: TicTacToeGameState, playerId: PlayerId): TicTacToeMove | null { + const validMoves = this.game.getValidMoves(gameState, playerId) for (const move of validMoves) { - const tempGameState = this.game.applyMove(gameState, move, playerId as any) + const tempGameState = this.game.applyMove(gameState, move, playerId) const result = this.game.checkGameEnd(tempGameState) if (result && result.winner === playerId) { diff --git a/mcp-server/src/handlers/elicitation-handlers.test.ts b/mcp-server/src/handlers/elicitation-handlers.test.ts new file mode 100644 index 0000000..e6fe9dc --- /dev/null +++ b/mcp-server/src/handlers/elicitation-handlers.test.ts @@ -0,0 +1,614 @@ +/** + * Tests for elicitation handlers + * + * These tests ensure that elicitation is properly triggered when game creation + * parameters are missing, and skipped when all required details are provided. + */ + +import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest' +import { + elicitGameCreationPreferences, + elicitMidGameDecision, + elicitGameCompletionFeedback, + elicitStrategyPreference, + elicitErrorRecovery, + type ElicitationResult +} from './elicitation-handlers' +import { DIFFICULTIES, DEFAULT_PLAYER_NAME, DEFAULT_AI_DIFFICULTY, GAME_TYPES } from '@turn-based-mcp/shared' + +// Mock server for elicitInput calls +const mockServer = { + elicitInput: vi.fn() +} + +describe('Elicitation Handlers', () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + afterEach(() => { + vi.resetAllMocks() + }) + + describe('elicitGameCreationPreferences', () => { + describe('Tic-Tac-Toe Game', () => { + const gameType = 'tic-tac-toe' + + it('should skip elicitation when all required details are provided', async () => { + const existingArgs = { + difficulty: 'medium' as const, + playerSymbol: 'X' as const, + playerName: 'TestPlayer' + } + + const result = await elicitGameCreationPreferences(mockServer, gameType, existingArgs) + + expect(mockServer.elicitInput).not.toHaveBeenCalled() + expect(result.action).toBe('accept') + expect(result.content).toEqual(existingArgs) + }) + + it('should trigger elicitation for optional parameters when only required difficulty is provided', async () => { + const existingArgs = { + difficulty: 'hard' as const + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { playerSymbol: 'O', playerName: 'OptionalPlayer' } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, gameType, existingArgs) + + expect(mockServer.elicitInput).toHaveBeenCalledOnce() + + // Verify that the schema only includes missing optional properties + const calledWith = mockServer.elicitInput.mock.calls[0][0] + expect(calledWith.requestedSchema.properties).not.toHaveProperty('difficulty') + expect(calledWith.requestedSchema.properties).toHaveProperty('playerSymbol') + expect(calledWith.requestedSchema.properties).toHaveProperty('playerName') + expect(calledWith.requestedSchema.required).toEqual([]) // No required properties left + + // Result should merge existing args with elicitation response + expect(result.content).toEqual({ + difficulty: 'hard', + playerSymbol: 'O', + playerName: 'OptionalPlayer' + }) + }) + + it('should trigger elicitation when no details are provided', async () => { + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { difficulty: 'easy', playerSymbol: 'O', playerName: 'NewPlayer' } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, gameType) + + expect(mockServer.elicitInput).toHaveBeenCalledOnce() + expect(mockServer.elicitInput).toHaveBeenCalledWith({ + message: expect.stringContaining("Let's set up your tic tac-toe game!"), + requestedSchema: expect.objectContaining({ + type: 'object', + properties: expect.objectContaining({ + difficulty: expect.objectContaining({ + type: 'string', + enum: DIFFICULTIES + }), + playerSymbol: expect.objectContaining({ + type: 'string', + enum: ['X', 'O'] + }), + playerName: expect.objectContaining({ + type: 'string' + }) + }), + required: ['difficulty'] + }) + }) + expect(result).toEqual(elicitationResponse) + }) + + it('should trigger elicitation when some optional details are missing', async () => { + const existingArgs = { + playerName: 'PartialPlayer' + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { difficulty: 'medium' } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, gameType, existingArgs) + + expect(mockServer.elicitInput).toHaveBeenCalledOnce() + + // Verify that the schema only includes missing properties + const calledWith = mockServer.elicitInput.mock.calls[0][0] + expect(calledWith.requestedSchema.properties).not.toHaveProperty('playerName') + expect(calledWith.requestedSchema.properties).toHaveProperty('difficulty') + expect(calledWith.requestedSchema.properties).toHaveProperty('playerSymbol') + + // Result should merge existing args with elicitation response + expect(result.content).toEqual({ + playerName: 'PartialPlayer', + difficulty: 'medium' + }) + }) + + it('should handle elicitation failure gracefully', async () => { + const error = new Error('Elicitation failed') + mockServer.elicitInput.mockRejectedValue(error) + + const result = await elicitGameCreationPreferences(mockServer, gameType) + + expect(mockServer.elicitInput).toHaveBeenCalledOnce() + expect(result.action).toBe('accept') + expect(result.content).toEqual({ + difficulty: DEFAULT_AI_DIFFICULTY, + playerName: DEFAULT_PLAYER_NAME, + playerSymbol: 'X' + }) + }) + + it('should filter out empty string values from existing args', async () => { + const existingArgs = { + difficulty: '', + playerSymbol: 'X', + playerName: '' + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { difficulty: 'hard', playerName: 'FilledPlayer' } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, gameType, existingArgs) + + expect(mockServer.elicitInput).toHaveBeenCalledOnce() + + // Should elicit difficulty and playerName but not playerSymbol + const calledWith = mockServer.elicitInput.mock.calls[0][0] + expect(calledWith.requestedSchema.properties).toHaveProperty('difficulty') + expect(calledWith.requestedSchema.properties).toHaveProperty('playerName') + expect(calledWith.requestedSchema.properties).not.toHaveProperty('playerSymbol') + + expect(result.content).toEqual({ + difficulty: 'hard', + playerSymbol: 'X', + playerName: 'FilledPlayer' + }) + }) + }) + + describe('Rock-Paper-Scissors Game', () => { + const gameType = 'rock-paper-scissors' + + it('should skip elicitation when all details are provided', async () => { + const existingArgs = { + difficulty: 'hard' as const, + maxRounds: 5, + playerName: 'RPSPlayer' + } + + const result = await elicitGameCreationPreferences(mockServer, gameType, existingArgs) + + expect(mockServer.elicitInput).not.toHaveBeenCalled() + expect(result.action).toBe('accept') + expect(result.content).toEqual(existingArgs) + }) + + it('should trigger elicitation for optional parameters when only required difficulty is provided', async () => { + const existingArgs = { + difficulty: 'easy' as const + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { maxRounds: 5, playerName: 'OptionalRPSPlayer' } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, gameType, existingArgs) + + expect(mockServer.elicitInput).toHaveBeenCalledOnce() + + // Verify that the schema only includes missing optional properties + const calledWith = mockServer.elicitInput.mock.calls[0][0] + expect(calledWith.requestedSchema.properties).not.toHaveProperty('difficulty') + expect(calledWith.requestedSchema.properties).toHaveProperty('maxRounds') + expect(calledWith.requestedSchema.properties).toHaveProperty('playerName') + expect(calledWith.requestedSchema.required).toEqual([]) // No required properties left + + // Result should merge existing args with elicitation response + expect(result.content).toEqual({ + difficulty: 'easy', + maxRounds: 5, + playerName: 'OptionalRPSPlayer' + }) + }) + + it('should trigger elicitation when no details are provided', async () => { + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { difficulty: 'medium', maxRounds: 3, playerName: 'RPSNewPlayer' } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, gameType) + + expect(mockServer.elicitInput).toHaveBeenCalledOnce() + expect(mockServer.elicitInput).toHaveBeenCalledWith({ + message: expect.stringContaining("Let's set up your rock paper-scissors game!"), + requestedSchema: expect.objectContaining({ + type: 'object', + properties: expect.objectContaining({ + difficulty: expect.objectContaining({ + type: 'string', + enum: DIFFICULTIES + }), + maxRounds: expect.objectContaining({ + type: 'number', + minimum: 1, + maximum: 10 + }), + playerName: expect.objectContaining({ + type: 'string' + }) + }), + required: ['difficulty'] + }) + }) + expect(result).toEqual(elicitationResponse) + }) + + it('should trigger elicitation when some optional details are missing', async () => { + const existingArgs = { + maxRounds: 7 + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { difficulty: 'hard', playerName: 'PartialRPSPlayer' } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, gameType, existingArgs) + + expect(mockServer.elicitInput).toHaveBeenCalledOnce() + + // Verify that the schema only includes missing properties + const calledWith = mockServer.elicitInput.mock.calls[0][0] + expect(calledWith.requestedSchema.properties).not.toHaveProperty('maxRounds') + expect(calledWith.requestedSchema.properties).toHaveProperty('difficulty') + expect(calledWith.requestedSchema.properties).toHaveProperty('playerName') + + // Result should merge existing args with elicitation response + expect(result.content).toEqual({ + maxRounds: 7, + difficulty: 'hard', + playerName: 'PartialRPSPlayer' + }) + }) + + it('should handle elicitation failure gracefully', async () => { + const error = new Error('Network error') + mockServer.elicitInput.mockRejectedValue(error) + + const result = await elicitGameCreationPreferences(mockServer, gameType) + + expect(mockServer.elicitInput).toHaveBeenCalledOnce() + expect(result.action).toBe('accept') + expect(result.content).toEqual({ + difficulty: DEFAULT_AI_DIFFICULTY, + playerName: DEFAULT_PLAYER_NAME, + maxRounds: 3 + }) + }) + + it('should trigger elicitation for missing parameters when some args are null/undefined', async () => { + const existingArgs = { + difficulty: 'medium' as const, + maxRounds: null, + playerName: undefined + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { maxRounds: 4, playerName: 'FilledPlayer' } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, gameType, existingArgs) + + expect(mockServer.elicitInput).toHaveBeenCalledOnce() + + // Should elicit maxRounds and playerName since they're null/undefined + const calledWith = mockServer.elicitInput.mock.calls[0][0] + expect(calledWith.requestedSchema.properties).not.toHaveProperty('difficulty') + expect(calledWith.requestedSchema.properties).toHaveProperty('maxRounds') + expect(calledWith.requestedSchema.properties).toHaveProperty('playerName') + + expect(result.content).toEqual({ + difficulty: 'medium', + maxRounds: 4, + playerName: 'FilledPlayer' + }) + }) + }) + + describe('Invalid Game Type', () => { + it('should throw error for unsupported game type', async () => { + const invalidGameType = 'checkers' + + await expect( + elicitGameCreationPreferences(mockServer, invalidGameType) + ).rejects.toThrow('No elicitation schema defined for game type: checkers') + + expect(mockServer.elicitInput).not.toHaveBeenCalled() + }) + }) + + describe('Edge Cases', () => { + it('should handle user cancellation gracefully', async () => { + const elicitationResponse: ElicitationResult = { + action: 'cancel' + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, 'tic-tac-toe') + + expect(result.action).toBe('cancel') + expect(result.content).toBeUndefined() + }) + + it('should handle user decline gracefully', async () => { + const elicitationResponse: ElicitationResult = { + action: 'decline' + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, 'rock-paper-scissors') + + expect(result.action).toBe('decline') + expect(result.content).toBeUndefined() + }) + + it('should preserve existing args when user accepts partial input', async () => { + const existingArgs = { + playerName: 'ExistingPlayer', + difficulty: undefined // This should be elicited + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { difficulty: 'easy' } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCreationPreferences(mockServer, 'tic-tac-toe', existingArgs) + + expect(result.content).toEqual({ + playerName: 'ExistingPlayer', + difficulty: 'easy' + }) + }) + }) + }) + + describe('elicitMidGameDecision', () => { + it('should create proper schema for mid-game decisions', async () => { + const context = { + gameType: 'tic-tac-toe', + gameId: 'game123', + situation: 'The AI is about to win!', + options: [ + { value: 'continue', label: 'Keep Playing' }, + { value: 'hint', label: 'Get a Hint', description: 'Show me the best move' }, + { value: 'restart', label: 'Restart Game' } + ] + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { choice: 'hint', feedback: 'This is challenging!' } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitMidGameDecision(mockServer, context) + + expect(mockServer.elicitInput).toHaveBeenCalledWith({ + message: expect.stringContaining('tic tac-toe Game Decision'), + requestedSchema: expect.objectContaining({ + type: 'object', + properties: expect.objectContaining({ + choice: expect.objectContaining({ + type: 'string', + enum: ['continue', 'hint', 'restart'], + enumNames: ['Keep Playing', 'Get a Hint', 'Restart Game'] + }), + feedback: expect.objectContaining({ + type: 'string' + }) + }), + required: ['choice'] + }) + }) + expect(result).toEqual(elicitationResponse) + }) + + it('should handle elicitation failure with default choice', async () => { + const context = { + gameType: 'rock-paper-scissors', + gameId: 'rps456', + situation: 'Choose your strategy', + options: [ + { value: 'aggressive', label: 'Aggressive Play' }, + { value: 'defensive', label: 'Defensive Play' } + ] + } + mockServer.elicitInput.mockRejectedValue(new Error('Failed')) + + const result = await elicitMidGameDecision(mockServer, context) + + expect(result.action).toBe('accept') + expect(result.content).toEqual({ choice: 'aggressive' }) + }) + }) + + describe('elicitGameCompletionFeedback', () => { + it('should create proper schema for game completion feedback', async () => { + const context = { + gameType: 'tic-tac-toe', + gameId: 'game789', + result: 'win' as const, + difficulty: 'hard' + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { + difficultyFeedback: 'just_right', + playAgain: true, + gameTypeForNext: 'rock-paper-scissors', + comments: 'Great game!' + } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitGameCompletionFeedback(mockServer, context) + + expect(mockServer.elicitInput).toHaveBeenCalledWith({ + message: expect.stringContaining('🎉 Congratulations! You won!'), + requestedSchema: expect.objectContaining({ + type: 'object', + properties: expect.objectContaining({ + difficultyFeedback: expect.objectContaining({ + type: 'string', + enum: ['too_easy', 'just_right', 'too_hard'] + }), + playAgain: expect.objectContaining({ + type: 'boolean' + }), + gameTypeForNext: expect.objectContaining({ + type: 'string', + enum: ['same', ...GAME_TYPES] + }) + }), + required: ['difficultyFeedback', 'playAgain'] + }) + }) + expect(result).toEqual(elicitationResponse) + }) + + it('should handle different result types with correct messages', async () => { + const contexts = [ + { result: 'loss' as const, expectedMessage: '😅 Good game! The AI won this time.' }, + { result: 'draw' as const, expectedMessage: '🤝 It\'s a draw! Well played by both sides.' } + ] + + for (const { result, expectedMessage } of contexts) { + mockServer.elicitInput.mockResolvedValue({ action: 'accept', content: {} }) + + await elicitGameCompletionFeedback(mockServer, { + gameType: 'rock-paper-scissors', + gameId: 'test', + result, + difficulty: 'medium' + }) + + expect(mockServer.elicitInput).toHaveBeenCalledWith({ + message: expect.stringContaining(expectedMessage), + requestedSchema: expect.any(Object) + }) + + mockServer.elicitInput.mockClear() + } + }) + }) + + describe('elicitStrategyPreference', () => { + it('should create proper schema for strategy preferences', async () => { + const context = { + gameType: 'tic-tac-toe', + gameId: 'strategy123', + availableHints: ['basic', 'advanced'], + currentSituation: 'You have a winning move available' + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { wantHint: true, hintType: 'intermediate', explainMoves: true } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitStrategyPreference(mockServer, context) + + expect(mockServer.elicitInput).toHaveBeenCalledWith({ + message: expect.stringContaining('Strategy Assistance Available'), + requestedSchema: expect.objectContaining({ + properties: expect.objectContaining({ + wantHint: expect.objectContaining({ type: 'boolean' }), + hintType: expect.objectContaining({ + type: 'string', + enum: ['beginner', 'intermediate', 'advanced'] + }) + }), + required: ['wantHint'] + }) + }) + expect(result).toEqual(elicitationResponse) + }) + }) + + describe('elicitErrorRecovery', () => { + it('should create proper schema for error recovery', async () => { + const context = { + gameType: 'rock-paper-scissors', + gameId: 'error123', + error: 'Invalid move detected', + recoveryOptions: [ + { value: 'retry', label: 'Try Again', description: 'Attempt the move again' }, + { value: 'reset', label: 'Reset Game', description: 'Start over from the beginning' } + ] + } + const elicitationResponse: ElicitationResult = { + action: 'accept', + content: { action: 'retry', reportIssue: true } + } + mockServer.elicitInput.mockResolvedValue(elicitationResponse) + + const result = await elicitErrorRecovery(mockServer, context) + + expect(mockServer.elicitInput).toHaveBeenCalledWith({ + message: expect.stringContaining('⚠️ **rock paper-scissors Game Issue**'), + requestedSchema: expect.objectContaining({ + properties: expect.objectContaining({ + action: expect.objectContaining({ + type: 'string', + enum: ['retry', 'reset'], + enumNames: ['Try Again', 'Reset Game'] + }), + reportIssue: expect.objectContaining({ type: 'boolean' }) + }), + required: ['action'] + }) + }) + expect(result).toEqual(elicitationResponse) + }) + + it('should handle elicitation failure with default recovery option', async () => { + const context = { + gameType: 'tic-tac-toe', + gameId: 'error456', + error: 'Network timeout', + recoveryOptions: [ + { value: 'reconnect', label: 'Reconnect', description: 'Try to reconnect' } + ] + } + mockServer.elicitInput.mockRejectedValue(new Error('Failed')) + + const result = await elicitErrorRecovery(mockServer, context) + + expect(result.action).toBe('accept') + expect(result.content).toEqual({ + action: 'reconnect', + reportIssue: false + }) + }) + }) +}) diff --git a/mcp-server/src/handlers/elicitation-handlers.ts b/mcp-server/src/handlers/elicitation-handlers.ts new file mode 100644 index 0000000..ef47d3f --- /dev/null +++ b/mcp-server/src/handlers/elicitation-handlers.ts @@ -0,0 +1,360 @@ +/** + * MCP Elicitation handlers for user input collection + * Provides structured ways to gather user preferences and decisions + */ + +import { DIFFICULTIES, GAME_TYPES, DEFAULT_PLAYER_NAME, DEFAULT_AI_DIFFICULTY } from '@turn-based-mcp/shared' + +export interface ElicitationResult { + action: "accept" | "decline" | "cancel" + content?: Record +} + +/** + * Game creation preferences elicitation + */ +export async function elicitGameCreationPreferences( + server: { elicitInput: (args: { message: string; requestedSchema: unknown }) => Promise }, + gameType: string, + existingArgs?: Record +): Promise { + const schemas = { + 'tic-tac-toe': { + type: "object", + properties: { + difficulty: { + type: "string", + enum: DIFFICULTIES, + title: "AI Difficulty Level", + description: "How challenging should the AI opponent be?" + }, + playerSymbol: { + type: "string", + enum: ["X", "O"], + title: "Your Symbol", + description: "Do you want to be X (goes first) or O (goes second)?", + default: "X" + }, + playerName: { + type: "string", + title: "Player Name", + description: "What should we call you in the game?", + default: DEFAULT_PLAYER_NAME + } + }, + required: ["difficulty"] + }, + 'rock-paper-scissors': { + type: "object", + properties: { + difficulty: { + type: "string", + enum: DIFFICULTIES, + title: "AI Difficulty Level", + description: "How smart should the AI be at pattern recognition?" + }, + maxRounds: { + type: "number", + minimum: 1, + maximum: 10, + title: "Number of Rounds", + description: "How many rounds should we play?", + default: 3 + }, + playerName: { + type: "string", + title: "Player Name", + description: "What should we call you?", + default: DEFAULT_PLAYER_NAME + } + }, + required: ["difficulty"] + } + } + + const baseSchema = schemas[gameType as keyof typeof schemas] + if (!baseSchema) { + throw new Error(`No elicitation schema defined for game type: ${gameType}`) + } + + // Filter out properties that are already provided + const filteredSchema: Record = { ...baseSchema } + const filteredProperties: Record = { ...baseSchema.properties } + const filteredRequired = [...(baseSchema.required || [])] + + // Remove properties that already have values + if (existingArgs) { + Object.keys(existingArgs).forEach(key => { + if (existingArgs[key] !== undefined && existingArgs[key] !== null && existingArgs[key] !== '') { + delete filteredProperties[key] + // Remove from required array if present + const requiredIndex = filteredRequired.indexOf(key) + if (requiredIndex > -1) { + filteredRequired.splice(requiredIndex, 1) + } + } + }) + } + + filteredSchema.properties = filteredProperties + filteredSchema.required = filteredRequired + + // If no properties remain to be elicited, skip elicitation + if (Object.keys(filteredProperties).length === 0) { + return { + action: "accept", + content: existingArgs || {} + } + } + + const message = `Let's set up your ${gameType.replace('-', ' ')} game! 🎮\n\nI'll need a few preferences to customize your experience:` + + try { + const result = await server.elicitInput({ + message, + requestedSchema: filteredSchema + }) + + // Merge the elicitation result with existing arguments + if (result.action === 'accept' && result.content) { + result.content = { ...existingArgs, ...result.content } + } + + return result + } catch (error) { + console.error('Elicitation failed:', error) + // Return default preferences if elicitation fails + return { + action: "accept", + content: { + difficulty: existingArgs?.difficulty || DEFAULT_AI_DIFFICULTY, + playerName: existingArgs?.playerName || DEFAULT_PLAYER_NAME, + ...(gameType === 'rock-paper-scissors' && { maxRounds: existingArgs?.maxRounds || 3 }), + ...(gameType === 'tic-tac-toe' && { playerSymbol: existingArgs?.playerSymbol || "X" }) + } + } + } +} + +/** + * Mid-game decision elicitation + */ +export async function elicitMidGameDecision( + server: { elicitInput: (args: { message: string; requestedSchema: unknown }) => Promise }, + context: { + gameType: string + gameId: string + situation: string + options: Array<{ value: string; label: string; description?: string }> + } +): Promise { + const { gameType, situation, options } = context + + const schema = { + type: "object", + properties: { + choice: { + type: "string", + enum: options.map(opt => opt.value), + enumNames: options.map(opt => opt.label), + title: "Your Choice", + description: "What would you like to do?" + }, + feedback: { + type: "string", + title: "Any feedback? (Optional)", + description: "Let me know if you have any thoughts about the game so far" + } + }, + required: ["choice"] + } + + const message = `🤔 **${gameType.replace('-', ' ')} Game Decision**\n\n${situation}\n\nWhat would you like to do?` + + try { + return await server.elicitInput({ + message, + requestedSchema: schema + }) + } catch (error) { + console.error('Mid-game elicitation failed:', error) + // Return first option as default + return { + action: "accept", + content: { choice: options[0]?.value || "continue" } + } + } +} + +/** + * Game completion feedback elicitation + */ +export async function elicitGameCompletionFeedback( + server: { elicitInput: (args: { message: string; requestedSchema: unknown }) => Promise }, + context: { + gameType: string + gameId: string + result: 'win' | 'loss' | 'draw' + difficulty: string + } +): Promise { + const { gameType, result, difficulty } = context + + const resultMessages = { + win: "🎉 Congratulations! You won!", + loss: "😅 Good game! The AI won this time.", + draw: "🤝 It's a draw! Well played by both sides." + } + + const schema = { + type: "object", + properties: { + difficultyFeedback: { + type: "string", + enum: ["too_easy", "just_right", "too_hard"], + enumNames: ["Too Easy", "Just Right", "Too Hard"], + title: "How was the difficulty?", + description: `The AI was set to ${difficulty} difficulty` + }, + playAgain: { + type: "boolean", + title: "Play another game?", + description: "Would you like to start a new game?" + }, + gameTypeForNext: { + type: "string", + enum: ["same", ...GAME_TYPES], + enumNames: ["Same Game", "Tic-Tac-Toe", "Rock Paper Scissors"], + title: "If playing again, which game?", + description: "Choose the game type for your next match" + }, + comments: { + type: "string", + title: "Any comments? (Optional)", + description: "Share your thoughts about the game experience" + } + }, + required: ["difficultyFeedback", "playAgain"] + } + + const message = `${resultMessages[result]}\n\n**Game Complete: ${gameType.replace('-', ' ')}**\n\nI'd love to get your feedback to improve future games:` + + try { + return await server.elicitInput({ + message, + requestedSchema: schema + }) + } catch (error) { + console.error('Completion feedback elicitation failed:', error) + return { + action: "decline", + content: {} + } + } +} + +/** + * Strategy hint elicitation + */ +export async function elicitStrategyPreference( + server: { elicitInput: (args: { message: string; requestedSchema: unknown }) => Promise }, + context: { + gameType: string + gameId: string + availableHints: string[] + currentSituation: string + } +): Promise { + const { currentSituation } = context + + const schema = { + type: "object", + properties: { + wantHint: { + type: "boolean", + title: "Would you like a strategy hint?", + description: "I can provide some strategic advice for this situation" + }, + hintType: { + type: "string", + enum: ["beginner", "intermediate", "advanced"], + enumNames: ["Basic Tips", "Strategic Insights", "Advanced Analysis"], + title: "What level of hint?", + description: "Choose the depth of strategic advice" + }, + explainMoves: { + type: "boolean", + title: "Explain possible moves?", + description: "Would you like me to analyze the available options?" + } + }, + required: ["wantHint"] + } + + const message = `🧠 **Strategy Assistance Available**\n\n**Current situation:** ${currentSituation}\n\nI can provide strategic guidance if you'd like:` + + try { + return await server.elicitInput({ + message, + requestedSchema: schema + }) + } catch (error) { + console.error('Strategy elicitation failed:', error) + return { + action: "decline", + content: { wantHint: false } + } + } +} + +/** + * Error recovery elicitation + */ +export async function elicitErrorRecovery( + server: { elicitInput: (args: { message: string; requestedSchema: unknown }) => Promise }, + context: { + gameType: string + gameId: string + error: string + recoveryOptions: Array<{ value: string; label: string; description: string }> + } +): Promise { + const { gameType, error, recoveryOptions } = context + + const schema = { + type: "object", + properties: { + action: { + type: "string", + enum: recoveryOptions.map(opt => opt.value), + enumNames: recoveryOptions.map(opt => opt.label), + title: "How should we handle this?", + description: "Choose your preferred recovery option" + }, + reportIssue: { + type: "boolean", + title: "Report this issue for improvement?", + description: "Help us improve by reporting this problem" + } + }, + required: ["action"] + } + + const message = `⚠️ **${gameType.replace('-', ' ')} Game Issue**\n\n**Problem:** ${error}\n\nHow would you like to proceed?` + + try { + return await server.elicitInput({ + message, + requestedSchema: schema + }) + } catch (error) { + console.error('Error recovery elicitation failed:', error) + return { + action: "accept", + content: { + action: recoveryOptions[0]?.value || "retry", + reportIssue: false + } + } + } +} diff --git a/mcp-server/src/handlers/game-operations.ts b/mcp-server/src/handlers/game-operations.ts index c5e61eb..9cea2fd 100644 --- a/mcp-server/src/handlers/game-operations.ts +++ b/mcp-server/src/handlers/game-operations.ts @@ -4,26 +4,30 @@ import { TicTacToeAI } from '../ai/tic-tac-toe-ai.js' import { RockPaperScissorsAI } from '../ai/rock-paper-scissors-ai.js' -import { TicTacToeGame, RockPaperScissorsGame } from '@turn-based-mcp/shared' +import { TicTacToeGame, type TicTacToeGameState, type RPSGameState } from '@turn-based-mcp/shared' import { getGameViaAPI, submitMoveViaAPI, createGameViaAPI } from '../utils/http-client.js' +import { DEFAULT_PLAYER_NAME, DEFAULT_AI_DIFFICULTY } from '@turn-based-mcp/shared' // Initialize AI and game instances const ticTacToeAI = new TicTacToeAI() const rpsAI = new RockPaperScissorsAI() const ticTacToeGame = new TicTacToeGame() -const rpsGame = new RockPaperScissorsGame() +// rpsGame instance not needed directly in this module (AI handles logic) /** * Helper function to read game resource */ -export async function readGameResource(gameType: string, gameId: string) { +type SupportedDifficulty = 'easy' | 'medium' | 'hard' +interface GameSessionWrapper { gameState: TicTacToeGameState | RPSGameState; difficulty?: SupportedDifficulty; history?: unknown[] } +export async function readGameResource(gameType: string, gameId: string): Promise { const uri = `game://${gameType}/${gameId}` try { - const gameSession = await getGameViaAPI(gameType, gameId) - if (!gameSession) { + const gameSession = await getGameViaAPI(gameType, gameId) + if (!gameSession || !gameSession.gameState) { throw new Error(`Game not found: ${uri}`) } - return gameSession + // Cast only after verifying presence + return gameSession as unknown as GameSessionWrapper } catch (error) { throw new Error(`Failed to read game resource ${uri}: ${error}`) } @@ -32,12 +36,12 @@ export async function readGameResource(gameType: string, gameId: string) { /** * Generic play game function */ -export async function playGame(gameType: string, gameId: string) { +export async function playGame(gameType: string, gameId: string): Promise> { // Get current game state via resource const gameSession = await readGameResource(gameType, gameId) // Use the difficulty stored in the game session, or fall back to medium - const difficulty = gameSession.aiDifficulty || 'medium' + const difficulty = gameSession.difficulty || 'medium' // Check if it's AI's turn if (gameSession.gameState.currentPlayerId !== 'ai') { @@ -49,21 +53,23 @@ export async function playGame(gameType: string, gameId: string) { throw new Error(`Game is not in playing state. Current status: ${gameSession.gameState.status}`) } - let aiMove: any + let aiMove: { row: number; col: number } | { choice: string } | undefined let moveDescription: string // Calculate AI move based on game type switch (gameType) { - case 'tic-tac-toe': - aiMove = await ticTacToeAI.makeMove(gameSession.gameState, difficulty as any) - moveDescription = `AI made move at row ${aiMove.row + 1}, col ${aiMove.col + 1}` + case 'tic-tac-toe': { + const move = await ticTacToeAI.makeMove(gameSession.gameState as TicTacToeGameState, difficulty as SupportedDifficulty) + aiMove = move + moveDescription = `AI made move at row ${move.row + 1}, col ${move.col + 1}` break - - case 'rock-paper-scissors': - const aiChoice = await rpsAI.makeChoice(gameSession.gameState, difficulty as any) + } + case 'rock-paper-scissors': { + const aiChoice = await rpsAI.makeChoice(gameSession.gameState as RPSGameState, difficulty as SupportedDifficulty) aiMove = { choice: aiChoice } moveDescription = `AI chose ${aiMove.choice}` break + } default: throw new Error(`Unsupported game type: ${gameType}`) @@ -73,7 +79,7 @@ export async function playGame(gameType: string, gameId: string) { const updatedGameSession = await submitMoveViaAPI(gameType, gameId, aiMove, 'ai') // Format response based on game type - const response: any = { + const response: Record = { gameId, gameType, difficulty, @@ -87,7 +93,9 @@ export async function playGame(gameType: string, gameId: string) { // Add game-specific move details switch (gameType) { case 'tic-tac-toe': - response.aiMove = { row: aiMove.row, col: aiMove.col } + if (aiMove && 'row' in aiMove) { + response.aiMove = { row: aiMove.row, col: aiMove.col } + } break case 'rock-paper-scissors': response.aiMove = aiMove @@ -105,13 +113,13 @@ export async function playGame(gameType: string, gameId: string) { /** * Generic analyze game function */ -export async function analyzeGame(gameType: string, gameId: string) { +export async function analyzeGame(gameType: string, gameId: string): Promise> { // Get current game state via resource const gameSession = await readGameResource(gameType, gameId) - const gameState = gameSession.gameState + const gameState = gameSession.gameState as TicTacToeGameState | RPSGameState const history = gameSession.history || [] - let analysis: any = { + const analysis: { [k: string]: unknown } = { gameId, gameType, status: gameState.status, @@ -125,20 +133,23 @@ export async function analyzeGame(gameType: string, gameId: string) { switch (gameType) { case 'tic-tac-toe': - analysis.boardState = gameState.board - analysis.playerSymbols = gameState.playerSymbols - analysis.validMoves = gameState.status === 'playing' ? ticTacToeGame.getValidMoves(gameState, gameState.currentPlayerId) : [] + { + const tState = gameState as TicTacToeGameState + analysis.boardState = tState.board + analysis.playerSymbols = tState.playerSymbols + analysis.validMoves = tState.status === 'playing' ? ticTacToeGame.getValidMoves(tState, tState.currentPlayerId) : [] if (gameState.status === 'playing') { - analysisText += `Current Turn: ${gameState.currentPlayerId} (${gameState.playerSymbols[gameState.currentPlayerId]})\n` - analysisText += `Valid Moves: ${analysis.validMoves.length} available\n` + analysisText += `Current Turn: ${tState.currentPlayerId} (${tState.playerSymbols[tState.currentPlayerId]})\n` + const validMoves = analysis.validMoves as Array + analysisText += `Valid Moves: ${validMoves.length} available\n` // Board visualization analysisText += '\nCurrent Board:\n' for (let row = 0; row < 3; row++) { let rowStr = '' for (let col = 0; col < 3; col++) { - const cell = gameState.board[row][col] + const cell = tState.board[row][col] rowStr += cell ? ` ${cell} ` : ' ' if (col < 2) rowStr += '|' } @@ -146,38 +157,41 @@ export async function analyzeGame(gameType: string, gameId: string) { if (row < 2) analysisText += '-----------\n' } - analysisText += gameState.currentPlayerId === 'ai' + analysisText += tState.currentPlayerId === 'ai' ? '\nIt\'s the AI\'s turn to move.' : '\nWaiting for human player to make a move.' - } else if (gameState.status === 'finished') { - analysisText += `Winner: ${gameState.winner || 'Draw'}\n` + } else if (tState.status === 'finished') { + analysisText += `Winner: ${tState.winner || 'Draw'}\n` analysisText += `Total moves played: ${history.length}\n` } + } break case 'rock-paper-scissors': - analysis.currentRound = gameState.currentRound - analysis.maxRounds = gameState.maxRounds - analysis.scores = gameState.scores - analysis.rounds = gameState.rounds + { + const rState = gameState as RPSGameState + analysis.currentRound = rState.currentRound + analysis.maxRounds = rState.maxRounds + analysis.scores = rState.scores + analysis.rounds = rState.rounds - analysisText += `Round: ${gameState.currentRound}/${gameState.maxRounds}\n` - analysisText += `Scores: Player: ${gameState.scores.player1 || 0}, AI: ${gameState.scores.ai || 0}\n\n` + analysisText += `Round: ${rState.currentRound}/${rState.maxRounds}\n` + analysisText += `Scores: Player: ${rState.scores.player1 || 0}, AI: ${rState.scores.ai || 0}\n\n` - if (gameState.status === 'playing') { - analysisText += `Current Turn: ${gameState.currentPlayerId}\n` + if (rState.status === 'playing') { + analysisText += `Current Turn: ${rState.currentPlayerId}\n` - const currentRoundData = gameState.rounds[gameState.currentRound - 1] - if (currentRoundData) { + const currentRoundData = rState.rounds[rState.currentRound - 1] + if (currentRoundData) { const player1HasChoice = !!currentRoundData.player1Choice const player2HasChoice = !!currentRoundData.player2Choice if (player1HasChoice && player2HasChoice) { - analysisText += `Round ${gameState.currentRound} Choices:\n` + analysisText += `Round ${rState.currentRound} Choices:\n` analysisText += `- Player: ${currentRoundData.player1Choice}\n` analysisText += `- AI: ${currentRoundData.player2Choice}\n` } else { - analysisText += `Round ${gameState.currentRound} Status:\n` + analysisText += `Round ${rState.currentRound} Status:\n` if (player1HasChoice || player2HasChoice) { analysisText += `- Some players have made their choices\n` } else { @@ -186,19 +200,20 @@ export async function analyzeGame(gameType: string, gameId: string) { } } - analysisText += gameState.currentPlayerId === 'ai' + analysisText += rState.currentPlayerId === 'ai' ? '\nIt\'s the AI\'s turn to make a choice.' : '\nWaiting for human player to make a choice.' - } else if (gameState.status === 'finished') { - analysisText += `Winner: ${gameState.winner || 'Draw'}\n` - analysisText += `Total rounds played: ${gameState.rounds.length}\n` + } else if (rState.status === 'finished') { + analysisText += `Winner: ${rState.winner || 'Draw'}\n` + analysisText += `Total rounds played: ${rState.rounds.length}\n` - analysisText += `\nRound Results:\n` - gameState.rounds.forEach((round: any, index: number) => { - if (round.player1Choice && round.player2Choice) { - analysisText += `Round ${index + 1}: Player (${round.player1Choice}) vs AI (${round.player2Choice}) - ${round.winner === 'draw' ? 'Draw' : `Winner: ${round.winner}`}\n` - } - }) + analysisText += `\nRound Results:\n` + rState.rounds.forEach((round, index: number) => { + if (round.player1Choice && round.player2Choice) { + analysisText += `Round ${index + 1}: Player (${round.player1Choice}) vs AI (${round.player2Choice}) - ${round.winner === 'draw' ? 'Draw' : `Winner: ${round.winner}`}\n` + } + }) + } } break @@ -218,7 +233,7 @@ export async function waitForPlayerMove( gameId: string, timeoutSeconds: number = 15, pollInterval: number = 3 -) { +): Promise> { const gameTypeNames: { [key: string]: string } = { 'tic-tac-toe': 'Tic-Tac-Toe', 'rock-paper-scissors': 'Rock Paper Scissors' @@ -230,7 +245,7 @@ export async function waitForPlayerMove( } // Get initial game state via resource - let currentGameSession = await readGameResource(gameType, gameId) + const currentGameSession = await readGameResource(gameType, gameId) // Check if game is finished if (currentGameSession.gameState.status === 'finished') { @@ -316,16 +331,17 @@ export async function waitForPlayerMove( */ export async function createGame( gameType: string, - playerName: string = 'Player', + playerName: string = DEFAULT_PLAYER_NAME, gameId?: string, - aiDifficulty: string = 'medium' -) { + difficulty: string = DEFAULT_AI_DIFFICULTY, + gameSpecificOptions?: Record +): Promise> { // Check if game already exists (for games that support custom IDs) if (gameId && gameType === 'tic-tac-toe') { try { const existingGame = await readGameResource(gameType, gameId) if (existingGame) { - const response: any = { + const response: Record = { gameId, gameType, message: `Found existing ${gameType} game with ID: ${gameId}`, @@ -335,16 +351,16 @@ export async function createGame( return response } - } catch (error) { + } catch { // Game doesn't exist, continue with creation } } // Create new game via API - const gameSession = await createGameViaAPI(gameType, playerName, gameId, aiDifficulty) + const gameSession = await createGameViaAPI(gameType, playerName, gameId, difficulty, gameSpecificOptions) - const response: any = { - gameId: gameSession.gameState.id, + const response: Record = { + gameId: gameSession.gameState.id as string, gameType, gameState: gameSession.gameState, players: gameSession.gameState.players, diff --git a/mcp-server/src/handlers/prompt-handlers.test.ts b/mcp-server/src/handlers/prompt-handlers.test.ts index c17a50b..8126153 100644 --- a/mcp-server/src/handlers/prompt-handlers.test.ts +++ b/mcp-server/src/handlers/prompt-handlers.test.ts @@ -69,7 +69,7 @@ describe('Prompt Handlers', () => { expect(message.content.type).toBe('text') expect(message.content.text).toContain('Please explain how to play Tic-Tac-Toe') expect(message.content.text).toContain('objective of the game') - expect(message.content.text).toContain('create_tic_tac_toe_game') + expect(message.content.text).toContain("create_game with gameType: 'tic-tac-toe'") }) it('should return rock-paper-scissors rules', async () => { @@ -78,7 +78,7 @@ describe('Prompt Handlers', () => { expect(result.messages[0].role).toBe('user') expect(result.messages[0].content.text).toContain('Please explain how to play Rock Paper Scissors') expect(result.messages[0].content.text).toContain('what beats what') - expect(result.messages[0].content.text).toContain('create_rock_paper_scissors_game') + expect(result.messages[0].content.text).toContain("create_game with gameType: 'rock-paper-scissors'") }) }) @@ -248,8 +248,8 @@ describe('Prompt Handlers', () => { const content = result.messages[0].content.text const gameType = gameName.replace('_rules', '').replace(/_/g, '-') - expect(content).toContain(`create_${gameName.replace('_rules', '_game')}`) - expect(content).toContain(`play_${gameName.replace('_rules', '')}`) + expect(content).toContain(`create_game with gameType: '${gameType}'`) + expect(content).toContain(`play_game with gameType: '${gameType}'`) } }) diff --git a/mcp-server/src/handlers/prompt-handlers.ts b/mcp-server/src/handlers/prompt-handlers.ts index 1b5f1e8..3e7e1ec 100644 --- a/mcp-server/src/handlers/prompt-handlers.ts +++ b/mcp-server/src/handlers/prompt-handlers.ts @@ -4,6 +4,7 @@ */ import type { Prompt, PromptMessage } from '@modelcontextprotocol/sdk/types.js' +import { GAME_TYPES, DIFFICULTIES } from '@turn-based-mcp/shared' export interface PromptDefinition { name: string @@ -13,7 +14,7 @@ export interface PromptDefinition { description: string required?: boolean }> - handler: (args?: Record) => Promise<{ + handler: (args?: Record) => Promise<{ description?: string messages: PromptMessage[] }> @@ -40,7 +41,7 @@ export const GAME_RULES_PROMPTS: PromptDefinition[] = [ 3. How to make moves (using positions 1-9) 4. All possible winning conditions 5. Basic strategy tips for beginners -6. How to use the MCP commands (create_tic_tac_toe_game, play_tic_tac_toe, wait_for_player_move) +6. How to use the MCP commands (create_game with gameType: 'tic-tac-toe', play_game with gameType: 'tic-tac-toe', wait_for_player_move) 7. What happens with perfect play Make it comprehensive but easy to understand for someone who has never played before.` @@ -66,7 +67,7 @@ Make it comprehensive but easy to understand for someone who has never played be 3. Strategy tips for beginners and advanced players 4. How psychology and pattern recognition work in this game 5. What the different AI difficulty levels mean and how to counter them -6. How to use the MCP commands (create_rock_paper_scissors_game, play_rock_paper_scissors, wait_for_player_move) +6. How to use the MCP commands (create_game with gameType: 'rock-paper-scissors', play_game with gameType: 'rock-paper-scissors', wait_for_player_move) 7. Why unpredictability is key to mastery Make it comprehensive and include both basic rules and advanced psychological strategies.` @@ -87,18 +88,18 @@ export const STRATEGY_PROMPTS: PromptDefinition[] = [ arguments: [ { name: 'gameType', - description: 'Game type (tic-tac-toe, rock-paper-scissors)', + description: `Game type (${GAME_TYPES?.join(', ') || 'tic-tac-toe, rock-paper-scissors'})`, required: false }, { name: 'difficulty', - description: 'AI difficulty level (easy, medium, hard)', + description: `AI difficulty level (${DIFFICULTIES?.join(', ') || 'easy, medium, hard'})`, required: false } ], - handler: async (args = {}) => { - const gameType = args.gameType?.toLowerCase() - const difficulty = args.difficulty?.toLowerCase() + handler: async (args: Record = {}): Promise<{ description: string; messages: PromptMessage[] }> => { + const gameType = typeof args.gameType === 'string' ? args.gameType.toLowerCase() : undefined + const difficulty = typeof args.difficulty === 'string' ? args.difficulty.toLowerCase() : undefined let content = `# AI Difficulty Strategy Guide\n\n` @@ -265,7 +266,7 @@ export async function listPrompts(): Promise<{ prompts: Prompt[] }> { } } -export async function getPrompt(name: string, args?: Record) { +export async function getPrompt(name: string, args?: Record): Promise<{ description?: string; messages: PromptMessage[] }> { const prompt = ALL_PROMPTS.find(p => p.name === name) if (!prompt) { throw new Error(`Prompt not found: ${name}`) @@ -296,7 +297,7 @@ function getSpecificStrategyGuide(gameType: string, difficulty: string): string function getGameTypeStrategies(gameType: string): string { // Return strategies for all difficulties of a specific game return `Please provide comprehensive strategies for ${gameType.toUpperCase()} across all difficulty levels:\n\n` + - ['easy', 'medium', 'hard'].map(diff => + DIFFICULTIES.map(diff => `For ${diff.toUpperCase()} difficulty: ${getSpecificStrategyGuide(gameType, diff)}\n` ).join('\n') } @@ -304,7 +305,7 @@ function getGameTypeStrategies(gameType: string): string { function getDifficultyStrategies(difficulty: string): string { // Return strategies for a specific difficulty across all games return `Please provide strategies for ${difficulty.toUpperCase()} difficulty across all games:\n\n` + - ['tic-tac-toe', 'rock-paper-scissors'].map(game => + GAME_TYPES.map(game => `For ${game.replace(/-/g, ' ').toUpperCase()}: ${getSpecificStrategyGuide(game, difficulty)}\n` ).join('\n') } diff --git a/mcp-server/src/handlers/resource-handlers.ts b/mcp-server/src/handlers/resource-handlers.ts index c592c9f..e3cc2a7 100644 --- a/mcp-server/src/handlers/resource-handlers.ts +++ b/mcp-server/src/handlers/resource-handlers.ts @@ -2,18 +2,17 @@ * MCP Resource handlers for game resources */ -import { getGamesByType, getGameViaAPI } from '../utils/http-client.js' - -const SUPPORTED_GAME_TYPES = ['tic-tac-toe', 'rock-paper-scissors'] +import { getGamesByType, getGameViaAPI, type GenericGameStateWrapper } from '../utils/http-client.js' +import { GAME_TYPES, isSupportedGameType } from '@turn-based-mcp/shared' /** * List all available game resources */ -export async function listResources() { +export async function listResources(): Promise<{ resources: Array> }> { try { const resources = [] - for (const gameType of SUPPORTED_GAME_TYPES) { + for (const gameType of GAME_TYPES) { try { // Get all games of this type const games = await getGamesByType(gameType) @@ -53,9 +52,9 @@ export async function listResources() { /** * Read a specific game resource */ -export async function readResource(uri: string) { +export async function readResource(uri: string): Promise<{ contents: Array<{ uri: string; mimeType: string; text: string }> }> { // Parse game resource URI: game://{gameType} or game://{gameType}/{gameId} - const match = uri.match(/^game:\/\/([^\/]+)(?:\/([^\/]+))?$/) + const match = uri.match(/^game:\/\/([^/]+)(?:\/([^/]+))?$/) if (!match) { throw new Error(`Invalid game resource URI: ${uri}`) } @@ -63,7 +62,7 @@ export async function readResource(uri: string) { const [, gameType, gameId] = match // Validate game type - if (!SUPPORTED_GAME_TYPES.includes(gameType)) { + if (!isSupportedGameType(gameType)) { throw new Error(`Invalid game type: ${gameType}`) } @@ -102,16 +101,25 @@ export async function readResource(uri: string) { mimeType: 'application/json', text: JSON.stringify({ gameType, - games: games.map((game: any) => ({ - gameId: game.gameState?.id, - status: game.gameState?.status, - currentPlayer: game.gameState?.currentPlayerId, - winner: game.gameState?.winner || null, - createdAt: game.gameState?.createdAt, - updatedAt: game.gameState?.updatedAt, - playerCount: Object.keys(game.gameState?.players || {}).length, - aiDifficulty: game.aiDifficulty - })), + games: games.map((game: GenericGameStateWrapper) => { + const playersUnknown: unknown = (game.gameState as Record).players + let playerCount = 0 + if (Array.isArray(playersUnknown)) { + playerCount = playersUnknown.length + } else if (playersUnknown && typeof playersUnknown === 'object') { + playerCount = Object.keys(playersUnknown as Record).length + } + return { + gameId: game.gameState.id, + status: game.gameState.status, + currentPlayer: game.gameState.currentPlayerId, + winner: game.gameState.winner || null, + createdAt: game.gameState.createdAt, + updatedAt: game.gameState.updatedAt, + playerCount, + difficulty: game.difficulty + } + }), totalGames: games.length, timestamp: new Date().toISOString() }) diff --git a/mcp-server/src/handlers/tool-handlers.ts b/mcp-server/src/handlers/tool-handlers.ts index f0bbe51..b397eaa 100644 --- a/mcp-server/src/handlers/tool-handlers.ts +++ b/mcp-server/src/handlers/tool-handlers.ts @@ -3,34 +3,27 @@ */ import { playGame, analyzeGame, waitForPlayerMove, createGame } from './game-operations.js' +import { elicitGameCreationPreferences } from './elicitation-handlers.js' +import { GAME_TYPES, DIFFICULTIES, isSupportedGameType, DEFAULT_PLAYER_NAME, DEFAULT_AI_DIFFICULTY } from '@turn-based-mcp/shared' export const TOOL_DEFINITIONS = [ { - name: 'play_tic_tac_toe', - description: 'Make an AI move in Tic-Tac-Toe game. IMPORTANT: After calling this tool when the game is still playing, you MUST call wait_for_player_move to continue the game flow.', + name: 'play_game', + description: 'Make an AI move in a game. IMPORTANT: After calling this tool when the game is still playing, you MUST call wait_for_player_move to continue the game flow.', inputSchema: { type: 'object', properties: { gameId: { type: 'string', - description: 'The ID of the Tic-Tac-Toe game to play', + description: 'The ID of the game to play', }, - }, - required: ['gameId'], - }, - }, - { - name: 'play_rock_paper_scissors', - description: 'Make an AI choice in Rock Paper Scissors game. IMPORTANT: After calling this tool when the game is still playing, you MUST call wait_for_player_move to continue the game flow.', - inputSchema: { - type: 'object', - properties: { - gameId: { + gameType: { type: 'string', - description: 'The ID of the Rock Paper Scissors game to play', + enum: GAME_TYPES, + description: 'Type of game to play', }, }, - required: ['gameId'], + required: ['gameId', 'gameType'], }, }, { @@ -45,7 +38,7 @@ export const TOOL_DEFINITIONS = [ }, gameType: { type: 'string', - enum: ['tic-tac-toe', 'rock-paper-scissors'], + enum: GAME_TYPES, description: 'Type of game to analyze', }, }, @@ -54,7 +47,7 @@ export const TOOL_DEFINITIONS = [ }, { name: 'wait_for_player_move', - description: 'Wait for human player to make their move after AI has played. This tool should be called after any play_* tool when the game is still ongoing.', + description: 'Wait for human player to make their move after AI has played. This tool should be called after the play_game tool when the game is still ongoing.', inputSchema: { type: 'object', properties: { @@ -64,7 +57,7 @@ export const TOOL_DEFINITIONS = [ }, gameType: { type: 'string', - enum: ['tic-tac-toe', 'rock-paper-scissors'], + enum: GAME_TYPES, description: 'Type of game to monitor', }, timeoutSeconds: { @@ -82,75 +75,72 @@ export const TOOL_DEFINITIONS = [ }, }, { - name: 'create_tic_tac_toe_game', - description: 'Create a new Tic-Tac-Toe game with optional custom game ID', + name: 'create_game', + description: 'Create a new game with interactive setup. This will ask you for preferences like difficulty, player options, and other game-specific settings. IMPORTANT: Only provide parameters that the user explicitly specified. DO NOT provide default values for optional parameters - missing parameters will trigger interactive elicitation.', inputSchema: { type: 'object', properties: { - playerName: { + gameType: { type: 'string', - description: 'Name of the human player', - default: 'Player', + enum: GAME_TYPES, + description: 'Type of game to create' }, gameId: { type: 'string', - description: 'Optional custom game ID. If not provided, a random UUID will be generated.', + description: 'Optional custom game ID. If not provided, a random UUID will be generated.' }, - aiDifficulty: { + difficulty: { type: 'string', - enum: ['easy', 'medium', 'hard'], - description: 'AI difficulty level', - default: 'medium', + enum: DIFFICULTIES, + description: 'AI difficulty level (easy, medium, hard). If not provided, will be asked during setup.' }, - }, - required: [], - }, - }, - { - name: 'create_rock_paper_scissors_game', - description: 'Create a new Rock Paper Scissors game', - inputSchema: { - type: 'object', - properties: { playerName: { type: 'string', - description: 'Name of the human player', - default: 'Player', + description: 'Your name in the game. LEAVE EMPTY to trigger interactive setup - do not provide defaults like "Player" or "User".' }, - aiDifficulty: { + playerSymbol: { type: 'string', - enum: ['easy', 'medium', 'hard'], - description: 'AI difficulty level', - default: 'medium', + enum: ['X', 'O'], + description: 'For tic-tac-toe: your symbol (X goes first, O goes second). LEAVE EMPTY to trigger interactive setup - do not auto-select X.' }, + maxRounds: { + type: 'number', + minimum: 1, + maximum: 10, + description: 'For rock-paper-scissors: number of rounds to play. If not provided, will be asked during setup.' + } }, - required: [], - }, + required: ['gameType'] + } }, ] /** * Handle tool execution */ -export async function handleToolCall(name: string, args: any) { - try { +// Match the SDK server's elicitInput signature loosely to avoid incompatibility issues. +export interface ServerWithElicit { + // Allow extra properties on args to accommodate SDK's added metadata fields. + elicitInput: (args: { message: string; requestedSchema: unknown; [k: string]: unknown }) => Promise<{ action: 'accept' | 'decline' | 'cancel'; content?: Record }> +} + +export async function handleToolCall(name: string, args: Record, server?: ServerWithElicit): Promise { switch (name) { - case 'play_tic_tac_toe': - const { gameId: ticTacToeGameId } = args - if (!ticTacToeGameId) { + case 'play_game': { + const { gameId: playGameId, gameType: playGameType } = args as { gameId?: string; gameType?: string } + if (!playGameId) { throw new Error('gameId is required') } - return await playGame('tic-tac-toe', ticTacToeGameId) - - case 'play_rock_paper_scissors': - const { gameId: rpsGameId } = args - if (!rpsGameId) { - throw new Error('gameId is required') + if (!playGameType) { + throw new Error('gameType is required') } - return await playGame('rock-paper-scissors', rpsGameId) - - case 'analyze_game': - const { gameId: analyzeGameId, gameType: analyzeGameType } = args + if (!isSupportedGameType(playGameType)) { + throw new Error(`Unsupported game type: ${playGameType}`) + } + return await playGame(playGameType, playGameId) + } + case 'analyze_game': { + const { gameId: analyzeGameId, gameType: analyzeGameType } = args as { gameId?: string; gameType?: string } if (!analyzeGameId) { throw new Error('gameId is required') } @@ -158,14 +148,14 @@ export async function handleToolCall(name: string, args: any) { throw new Error('gameType is required') } return await analyzeGame(analyzeGameType, analyzeGameId) - - case 'wait_for_player_move': + } + case 'wait_for_player_move': { const { gameId: waitGameId, gameType: waitGameType, timeoutSeconds = 15, pollInterval = 3 - } = args + } = args as { gameId?: string; gameType?: string; timeoutSeconds?: number; pollInterval?: number } if (!waitGameId) { throw new Error('gameId is required') } @@ -173,26 +163,104 @@ export async function handleToolCall(name: string, args: any) { throw new Error('gameType is required') } return await waitForPlayerMove(waitGameType, waitGameId, timeoutSeconds, pollInterval) + } + case 'create_game': { + const { gameType: genericGameType, gameId: genericGameId } = args as { gameType?: string; gameId?: string } + if (!genericGameType) { + throw new Error('gameType is required') + } + if (!isSupportedGameType(genericGameType)) { + throw new Error(`Unsupported game type: ${genericGameType}`) + } + return await createGameWithElicitation(genericGameType, genericGameId, server, args) + } + default: + throw new Error(`Unknown tool: ${name}`) + } +} - case 'create_tic_tac_toe_game': - const { - playerName: ticTacToePlayerName = 'Player', - gameId: ticTacToeNewGameId, - aiDifficulty: ticTacToeAiDifficulty = 'medium' - } = args - return await createGame('tic-tac-toe', ticTacToePlayerName, ticTacToeNewGameId, ticTacToeAiDifficulty) +/** + * Create game with interactive elicitation + */ +async function createGameWithElicitation(gameType: string, gameId?: string, server?: ServerWithElicit, toolArgs?: Record): Promise { + if (!server) { + // Fallback to regular creation if no server for elicitation + return await createGame(gameType, DEFAULT_PLAYER_NAME, gameId, DEFAULT_AI_DIFFICULTY) + } - case 'create_rock_paper_scissors_game': - const { - playerName: rpsPlayerName = 'Player', - aiDifficulty: rpsAiDifficulty = 'medium' - } = args - return await createGame('rock-paper-scissors', rpsPlayerName, undefined, rpsAiDifficulty) + try { + // Validate toolArgs properties before passing them + const elicitationOptions: Record = { gameId }; + if (toolArgs) { + if (typeof toolArgs.playerName === 'string') { + elicitationOptions.playerName = toolArgs.playerName; + } + if (typeof toolArgs.difficulty === 'string') { + elicitationOptions.difficulty = toolArgs.difficulty; + } + if (typeof toolArgs.playerSymbol === 'string') { + elicitationOptions.playerSymbol = toolArgs.playerSymbol; + } + if (typeof toolArgs.maxRounds === 'number') { + elicitationOptions.maxRounds = toolArgs.maxRounds; + } + } + const elicitationResult = await elicitGameCreationPreferences(server, gameType, elicitationOptions) - default: - throw new Error(`Unknown tool: ${name}`) + if (elicitationResult.action === 'decline' || elicitationResult.action === 'cancel') { + return { + gameId: null, + gameType, + message: `🚫 Game creation ${elicitationResult.action}d by user`, + status: 'cancelled', + action: elicitationResult.action + } + } + + if (elicitationResult.action === 'accept' && elicitationResult.content) { + const { difficulty, playerName, playerSymbol, maxRounds } = elicitationResult.content + + // Prepare game creation parameters + const finalPlayerName = (playerName as string) || 'Player' + const finalDifficulty = (difficulty as string) || 'medium' + + // Prepare game-specific options + const gameSpecificOptions: Record = {} + if (gameType === 'tic-tac-toe' && playerSymbol) { + gameSpecificOptions.playerSymbol = playerSymbol + } + if (gameType === 'rock-paper-scissors' && maxRounds) { + gameSpecificOptions.maxRounds = maxRounds + } + + // Create the game with elicited preferences + const gameResult = await createGame(gameType, finalPlayerName, gameId, finalDifficulty, gameSpecificOptions) + + // Add elicitation information to the response + gameResult.elicitation = { + preferences: elicitationResult.content, + message: '🎮 Game created with your custom preferences!' + } + + // Add game-specific messages + if (gameType === 'tic-tac-toe' && playerSymbol) { + gameResult.message += ` You are playing as ${playerSymbol}.` + if (playerSymbol === 'X') { + gameResult.message += ' You go first!' + } else { + gameResult.message += ' AI goes first!' + } + } + if (gameType === 'rock-paper-scissors' && maxRounds) { + gameResult.message += ` Playing ${maxRounds} rounds.` + } + + return gameResult } } catch (error) { - throw error + console.warn('Interactive game creation failed, falling back to defaults:', error) } + + // Fallback to regular creation + return await createGame(gameType, DEFAULT_PLAYER_NAME, gameId, DEFAULT_AI_DIFFICULTY) } diff --git a/mcp-server/src/index.ts b/mcp-server/src/index.ts deleted file mode 100644 index b2d976f..0000000 --- a/mcp-server/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './server.js' diff --git a/mcp-server/src/integration/integration.test.ts b/mcp-server/src/integration/integration.test.ts index fd5aa12..087f6b1 100644 --- a/mcp-server/src/integration/integration.test.ts +++ b/mcp-server/src/integration/integration.test.ts @@ -1,46 +1,56 @@ +import { vi } from 'vitest' import { handleToolCall } from '../handlers/tool-handlers.js' import { listResources, readResource } from '../handlers/resource-handlers.js' import { listPrompts, getPrompt } from '../handlers/prompt-handlers.js' +import * as httpClient from '../utils/http-client.js' + +// Import the real constants from shared package +// Importing shared constants (some unused intentionally for integration scope) - remove to satisfy lint +// Removed unused imports // Mock the web API calls for testing -jest.mock('../utils/http-client.js', () => ({ - httpGet: jest.fn(), - httpPost: jest.fn(), - getGameViaAPI: jest.fn(), - createGameViaAPI: jest.fn(), - submitMoveViaAPI: jest.fn(), - getGamesByType: jest.fn() +vi.mock('../utils/http-client.js', () => ({ + httpGet: vi.fn(), + httpPost: vi.fn(), + getGameViaAPI: vi.fn(), + createGameViaAPI: vi.fn(), + submitMoveViaAPI: vi.fn(), + getGamesByType: vi.fn() })) -// Mock shared library -jest.mock('@turn-based-mcp/shared', () => ({ - TicTacToeGame: jest.fn(() => ({ - getValidMoves: jest.fn(() => [{ row: 0, col: 0 }]) - })), - RockPaperScissorsGame: jest.fn(() => ({})) -})) +// Mock only the game classes from shared library +vi.mock('@turn-based-mcp/shared', async (importOriginal) => { + const actual = await importOriginal() as any + return { + ...actual, + TicTacToeGame: vi.fn(() => ({ + getValidMoves: vi.fn(() => [{ row: 0, col: 0 }]) + })), + RockPaperScissorsGame: vi.fn(() => ({})) + } +}) // Mock AI modules -jest.mock('../ai/tic-tac-toe-ai.js', () => ({ - TicTacToeAI: jest.fn(() => ({ - makeMove: jest.fn(() => ({ row: 0, col: 0 })) +vi.mock('../ai/tic-tac-toe-ai.js', () => ({ + TicTacToeAI: vi.fn(() => ({ + makeMove: vi.fn(() => ({ row: 0, col: 0 })) })) })) -jest.mock('../ai/rock-paper-scissors-ai.js', () => ({ - RockPaperScissorsAI: jest.fn(() => ({ - makeChoice: jest.fn(() => 'rock') +vi.mock('../ai/rock-paper-scissors-ai.js', () => ({ + RockPaperScissorsAI: vi.fn(() => ({ + makeChoice: vi.fn(() => 'rock') })) })) describe('MCP Server Integration', () => { beforeEach(() => { - jest.clearAllMocks() + vi.clearAllMocks() }) describe('Resource Handlers', () => { it('should list resources correctly', async () => { - const mockGetGamesByType = require('../utils/http-client.js').getGamesByType + const mockGetGamesByType = vi.mocked(httpClient.getGamesByType) mockGetGamesByType.mockResolvedValue([ { gameState: { @@ -57,20 +67,16 @@ describe('MCP Server Integration', () => { expect(result.resources.length).toBeGreaterThan(0) // Should include game type resources - const gameTypeResources = result.resources.filter(r => - r.uri.match(/^game:\/\/[^\/]+$/) - ) + const gameTypeResources = (result.resources as Array<{ uri: string }>).filter(r => r.uri.match(/^game:\/\/[^/]+$/)) expect(gameTypeResources.length).toBe(2) // tic-tac-toe, rock-paper-scissors // Should include individual game resources - const individualGameResources = result.resources.filter(r => - r.uri.match(/^game:\/\/[^\/]+\/[^\/]+$/) - ) + const individualGameResources = (result.resources as Array<{ uri: string }>).filter(r => r.uri.match(/^game:\/\/[^/]+\/[^/]+$/)) expect(individualGameResources.length).toBeGreaterThan(0) }) it('should read game type resource correctly', async () => { - const mockGetGamesByType = require('../utils/http-client.js').getGamesByType + const mockGetGamesByType = vi.mocked(httpClient.getGamesByType) mockGetGamesByType.mockResolvedValue([ { gameState: { @@ -79,7 +85,7 @@ describe('MCP Server Integration', () => { currentPlayerId: 'player1', players: { player1: 'Human', ai: 'AI' } }, - aiDifficulty: 'medium' + difficulty: 'medium' } ]) @@ -95,7 +101,7 @@ describe('MCP Server Integration', () => { }) it('should read individual game resource correctly', async () => { - const mockGetGameViaAPI = require('../utils/http-client.js').getGameViaAPI + const mockGetGameViaAPI = vi.mocked(httpClient.getGameViaAPI) mockGetGameViaAPI.mockResolvedValue({ gameState: { id: 'test-game-1', @@ -118,7 +124,7 @@ describe('MCP Server Integration', () => { describe('Tool Handlers', () => { it('should create tic-tac-toe game correctly', async () => { - const mockCreateGameViaAPI = require('../utils/http-client.js').createGameViaAPI + const mockCreateGameViaAPI = vi.mocked(httpClient.createGameViaAPI) mockCreateGameViaAPI.mockResolvedValue({ gameState: { id: 'new-game-id', @@ -127,18 +133,18 @@ describe('MCP Server Integration', () => { } }) - const result = await handleToolCall('create_tic_tac_toe_game', { - playerName: 'Test Player', - aiDifficulty: 'medium' + const result = await handleToolCall('create_game', { + gameType: 'tic-tac-toe' }) - expect(result.gameId).toBe('new-game-id') - expect(result.message).toContain('Created new Tic-Tac-Toe game') + const created = result as any + expect(created.gameId).toBe('new-game-id') + expect(created.message).toContain('Created new Tic-Tac-Toe game') }) it('should handle play moves correctly', async () => { - const mockGetGameViaAPI = require('../utils/http-client.js').getGameViaAPI - const mockSubmitMoveViaAPI = require('../utils/http-client.js').submitMoveViaAPI + const mockGetGameViaAPI = vi.mocked(httpClient.getGameViaAPI) + const mockSubmitMoveViaAPI = vi.mocked(httpClient.submitMoveViaAPI) mockGetGameViaAPI.mockResolvedValue({ gameState: { @@ -146,7 +152,7 @@ describe('MCP Server Integration', () => { status: 'playing', currentPlayerId: 'ai' }, - aiDifficulty: 'medium' + difficulty: 'medium' }) mockSubmitMoveViaAPI.mockResolvedValue({ @@ -157,13 +163,15 @@ describe('MCP Server Integration', () => { } }) - const result = await handleToolCall('play_tic_tac_toe', { - gameId: 'test-game' + const result = await handleToolCall('play_game', { + gameId: 'test-game', + gameType: 'tic-tac-toe' }) - expect(result.gameId).toBe('test-game') - expect(result.aiMove).toBeDefined() - expect(result.message).toContain('AI made move') + const playResult = result as any + expect(playResult.gameId).toBe('test-game') + expect(playResult.aiMove).toBeDefined() + expect(playResult.message).toContain('AI made move') }) it('should handle invalid tool names', async () => { diff --git a/mcp-server/src/integration/mcp-rps-security.test.ts b/mcp-server/src/integration/mcp-rps-security.test.ts index 8b0b1cb..0de00fa 100644 --- a/mcp-server/src/integration/mcp-rps-security.test.ts +++ b/mcp-server/src/integration/mcp-rps-security.test.ts @@ -1,14 +1,15 @@ +import { vi } from 'vitest' import { getRPSGameForMCP, makeRPSMove } from '@turn-based-mcp/shared' import type { GameSession } from '@turn-based-mcp/shared' import type { RPSGameState, RPSMove } from '@turn-based-mcp/shared' // Mock fetch for testing -global.fetch = jest.fn() -const mockFetch = global.fetch as jest.MockedFunction +global.fetch = vi.fn() +const mockFetch = global.fetch as vi.MockedFunction describe('MCP Server RPS Security Integration', () => { beforeEach(() => { - jest.clearAllMocks() + vi.clearAllMocks() process.env.WEB_API_BASE = 'http://localhost:3000' }) diff --git a/mcp-server/src/server.ts b/mcp-server/src/server.ts index f3a0699..500d13d 100644 --- a/mcp-server/src/server.ts +++ b/mcp-server/src/server.ts @@ -13,7 +13,7 @@ import { // Import handlers import { listResources, readResource } from './handlers/resource-handlers.js' -import { TOOL_DEFINITIONS, handleToolCall } from './handlers/tool-handlers.js' +import { TOOL_DEFINITIONS, handleToolCall, type ServerWithElicit } from './handlers/tool-handlers.js' import { listPrompts, getPrompt } from './handlers/prompt-handlers.js' const server = new Server( @@ -26,6 +26,7 @@ const server = new Server( tools: {}, resources: {}, prompts: {}, + elicitation: {}, }, } ) @@ -70,7 +71,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { // conversational flow between moves. try { - const result = await handleToolCall(name, args) + const result = await handleToolCall(name, args ?? {}, server as unknown as ServerWithElicit) return { content: [ { @@ -93,7 +94,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => { }) // Start the server -async function main() { +async function main(): Promise { console.error('Starting Turn-based Games MCP server...') const transport = new StdioServerTransport() diff --git a/mcp-server/src/utils/http-client.ts b/mcp-server/src/utils/http-client.ts index 105e6ab..70dfdea 100644 --- a/mcp-server/src/utils/http-client.ts +++ b/mcp-server/src/utils/http-client.ts @@ -1,36 +1,46 @@ /** * HTTP client utilities for communicating with the web API + * + * This module provides higher-level API functions for the MCP server + * while using shared HTTP utilities to eliminate code duplication. */ -const WEB_API_BASE = process.env.WEB_API_BASE || 'http://localhost:3000' +import { httpGet, httpPost, WEB_API_BASE } from '@turn-based-mcp/shared' +import type { GameSession } from '@turn-based-mcp/shared' +import type { TicTacToeGameState, RPSGameState } from '@turn-based-mcp/shared' -export async function httpGet(url: string): Promise { - const response = await fetch(url) - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`) - } - return response.json() +// Union of supported game session types the MCP server cares about +export type SupportedGameSession = + | GameSession + | GameSession + +// Narrowed lightweight shape used internally when we just need core fields +type MinimalGameState = { + id: string + status: string + currentPlayerId: string + winner?: string | 'draw' + updatedAt: string | Date + createdAt: string | Date + [k: string]: unknown } -export async function httpPost(url: string, data: any): Promise { - const response = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(data) - }) - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`) - } - return response.json() +export interface GenericGameStateWrapper { + gameState: MinimalGameState & Record + difficulty?: string + history?: unknown[] + [k: string]: unknown } +interface CreateGameOptions { [k: string]: unknown } +interface MovePayload { choice?: string; row?: number; col?: number; [k: string]: unknown } /** * Generic game state fetcher for resources */ -export async function getGameViaAPI(gameType: string, gameId: string) { +export async function getGameViaAPI(gameType: string, gameId: string): Promise { try { const games = await httpGet(`${WEB_API_BASE}/api/games/${gameType}/mcp`) - return games.find((game: any) => game.gameState?.id === gameId) + return (games as GenericGameStateWrapper[]).find((game) => game.gameState?.id === gameId) } catch (error) { console.error(`Error fetching ${gameType} game via API:`, error) return undefined @@ -44,15 +54,20 @@ export async function createGameViaAPI( gameType: string, playerName: string, gameId?: string, - aiDifficulty?: string -) { + difficulty?: string, + gameSpecificOptions?: CreateGameOptions +): Promise { try { - const data: any = { playerName } + const data: Record = { playerName } if (gameId) { data.gameId = gameId } - if (aiDifficulty) { - data.aiDifficulty = aiDifficulty + if (difficulty) { + data.difficulty = difficulty + } + // Merge any game-specific options + if (gameSpecificOptions) { + Object.assign(data, gameSpecificOptions) } return await httpPost(`${WEB_API_BASE}/api/games/${gameType}`, data) } catch (error) { @@ -67,9 +82,9 @@ export async function createGameViaAPI( export async function submitMoveViaAPI( gameType: string, gameId: string, - move: any, + move: MovePayload, playerId: string -) { +): Promise { return await httpPost(`${WEB_API_BASE}/api/games/${gameType}/${gameId}/move`, { move, playerId @@ -79,7 +94,7 @@ export async function submitMoveViaAPI( /** * Get all games of a specific type */ -export async function getGamesByType(gameType: string) { +export async function getGamesByType(gameType: string): Promise { return await httpGet(`${WEB_API_BASE}/api/games/${gameType}/mcp`) } diff --git a/mcp-server/tsconfig.json b/mcp-server/tsconfig.json index b49cd9b..343f88c 100644 --- a/mcp-server/tsconfig.json +++ b/mcp-server/tsconfig.json @@ -12,8 +12,8 @@ "strict": true, "skipLibCheck": true, "esModuleInterop": true, - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true }, "include": ["src/**/*"], - "exclude": ["dist", "node_modules"] + "exclude": ["dist", "node_modules", "**/*.test.ts", "**/*.test.tsx"] } diff --git a/mcp-server/vitest.config.ts b/mcp-server/vitest.config.ts new file mode 100644 index 0000000..866ceeb --- /dev/null +++ b/mcp-server/vitest.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + environment: 'node', + globals: true, + setupFiles: ['./vitest.setup.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'lcov', 'html'], + thresholds: { + branches: 85, + functions: 85, + lines: 85, + statements: 85 + }, + include: ['src/**/*.{ts,tsx}'], + exclude: ['src/**/*.d.ts', 'src/**/index.ts'] + } + }, +}) diff --git a/mcp-server/vitest.setup.ts b/mcp-server/vitest.setup.ts new file mode 100644 index 0000000..7b3e7a8 --- /dev/null +++ b/mcp-server/vitest.setup.ts @@ -0,0 +1,56 @@ +/** + * Vitest setup for mcp-server package tests + * + * Uses shared test setup utilities to ensure consistent database setup + */ + +import { setupStandardTestDatabase } from '@turn-based-mcp/shared/testing' + +// Setup standard test database using shared utility +setupStandardTestDatabase() + +// Suppress expected console errors during tests +// These are typically from error handling scenarios that we're intentionally testing +const originalConsoleError = console.error +console.error = (...args: any[]) => { + const message = args[0] + + if (typeof message === 'string') { + // Suppress expected error messages from elicitation handlers + if ( + message.includes('Elicitation failed:') || + message.includes('Mid-game elicitation failed:') || + message.includes('Error recovery elicitation failed:') || + message.includes('Completion feedback elicitation failed:') || + message.includes('Strategy elicitation failed:') + ) { + return // Suppress these expected errors + } + + // Suppress expected error messages from resource handlers + if ( + message.includes('Error listing') && message.includes('games:') || + message.includes('Error listing resources:') + ) { + return // Suppress these expected errors + } + + // Suppress expected error messages from HTTP client + if ( + message.includes('Error fetching') && message.includes('game via API:') || + message.includes('Error creating') && message.includes('game via API:') + ) { + return // Suppress these expected errors + } + + // Suppress expected server error messages (but not startup messages) + if ( + message.includes('Server error:') + ) { + return // Suppress these expected errors + } + } + + // For all other errors, use the original console.error + originalConsoleError(...args) +} diff --git a/package-lock.json b/package-lock.json index a6c06e4..19ff948 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,14 +13,16 @@ "mcp-server" ], "devDependencies": { + "@eslint/js": "^9.32.0", "@testing-library/jest-dom": "^6.6.4", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", - "@types/node": "^24.1.0", - "jest": "^30.0.5", - "ts-jest": "^29.4.0", - "typescript": "^5.5.0" + "@types/node": "^24.2.0", + "@vitest/ui": "^3.2.4", + "eslint": "^9.32.0", + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0", + "vitest": "^3.2.4" }, "engines": { "node": ">=18.0.0" @@ -30,15 +32,16 @@ "name": "@turn-based-mcp/mcp-server", "version": "1.0.0", "dependencies": { - "@modelcontextprotocol/sdk": "^1.17.0", + "@modelcontextprotocol/sdk": "^1.17.1", "@turn-based-mcp/shared": "file:../shared" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/node": "^24.1.0", - "jest": "^30.0.5", - "ts-jest": "^29.4.0", - "typescript": "^5.5.0" + "@eslint/js": "^9.32.0", + "@types/node": "^24.2.0", + "@vitest/ui": "^3.2.4", + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0", + "vitest": "^3.2.4" } }, "node_modules/@adobe/css-tools": { @@ -152,19 +155,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/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/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -331,107 +321,10 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { + "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dev": true, "license": "MIT", "dependencies": { @@ -444,120 +337,10 @@ "@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": { + "node_modules/@babel/plugin-transform-react-jsx-source": { "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dev": true, "license": "MIT", "dependencies": { @@ -629,11 +412,14 @@ } }, "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==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/@csstools/color-helpers": { "version": "5.0.2", @@ -750,386 +536,601 @@ "node": ">=18" } }, - "node_modules/@edge-runtime/jest-environment": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@edge-runtime/jest-environment/-/jest-environment-4.0.0.tgz", - "integrity": "sha512-75bTOg+coJO0YRfwzlSzCxdNi7KRKcRBeCYSmnKrvYiS++iF2kif0TLL8oKu6Z70pcFO3ugC6AjOloLe0WxOzw==", + "node_modules/@emnapi/core": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", + "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@edge-runtime/vm": "5.0.0", - "@jest/environment": "29.5.0", - "@jest/fake-timers": "29.5.0", - "jest-mock": "29.5.0", - "jest-util": "29.5.0" - }, - "engines": { - "node": ">=18" + "@emnapi/wasi-threads": "1.0.4", + "tslib": "^2.4.0" } }, - "node_modules/@edge-runtime/jest-environment/node_modules/@jest/environment": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", - "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", - "dev": true, + "node_modules/@emnapi/runtime": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", "license": "MIT", + "optional": true, "dependencies": { - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-mock": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "tslib": "^2.4.0" } }, - "node_modules/@edge-runtime/jest-environment/node_modules/@jest/fake-timers": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", - "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", + "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@jest/types": "^29.5.0", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "tslib": "^2.4.0" } }, - "node_modules/@edge-runtime/jest-environment/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==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" } }, - "node_modules/@edge-runtime/jest-environment/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==", + "node_modules/@esbuild/android-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "cpu": [ + "arm" + ], "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" - }, + "optional": true, + "os": [ + "android" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@edge-runtime/jest-environment/node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@edge-runtime/jest-environment/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": ">=18" } }, - "node_modules/@edge-runtime/jest-environment/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==", + "node_modules/@esbuild/android-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18" } }, - "node_modules/@edge-runtime/jest-environment/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" - } + "node_modules/@esbuild/android-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "cpu": [ + "x64" ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@edge-runtime/jest-environment/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==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "cpu": [ + "arm64" + ], "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" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" } }, - "node_modules/@edge-runtime/jest-environment/node_modules/jest-mock": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", - "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-util": "^29.5.0" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" } }, - "node_modules/@edge-runtime/jest-environment/node_modules/jest-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" } }, - "node_modules/@edge-runtime/jest-environment/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==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=18" } }, - "node_modules/@edge-runtime/jest-environment/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==", + "node_modules/@esbuild/linux-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@edge-runtime/primitives": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@edge-runtime/primitives/-/primitives-6.0.0.tgz", - "integrity": "sha512-FqoxaBT+prPBHBwE1WXS1ocnu/VLTQyZ6NMUBAdbP7N2hsFTTxMC/jMu2D/8GAlMQfxeuppcPuCUk/HO3fpIvA==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=18" } }, - "node_modules/@edge-runtime/vm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@edge-runtime/vm/-/vm-5.0.0.tgz", - "integrity": "sha512-NKBGBSIKUG584qrS1tyxVpX/AKJKQw5HgjYEnPLC0QsTw79JrGn+qUr8CXFb955Iy7GUdiiUv1rJ6JBGvaKb6w==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "@edge-runtime/primitives": "6.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=18" } }, - "node_modules/@emnapi/core": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", - "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.4", - "tslib": "^2.4.0" + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@emnapi/runtime": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", - "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "cpu": [ + "mips64el" + ], + "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", - "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, + "optional": true, + "os": [ + "linux" + ], "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": ">=18" } }, - "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==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "cpu": [ + "s390x" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "node_modules/@esbuild/linux-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=18" } }, - "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">=18" } }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=18" } }, - "node_modules/@eslint/js": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", - "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" + "node": ">=18" } }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", @@ -1660,727 +1661,322 @@ "node": ">=12" } }, - "node_modules/@isaacs/fs-minipass": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", - "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "license": "ISC", - "dependencies": { - "minipass": "^7.0.4" - }, + "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?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==", + "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==", "dev": true, - "license": "ISC", + "license": "MIT", "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" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", "dependencies": { - "sprintf-js": "~1.0.2" + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "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==", + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "minipass": "^7.0.4" }, "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/@isaacs/fs-minipass/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "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==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, "license": "MIT", - "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==", + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", "dev": true, "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "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, + "node_modules/@jest/expect-utils": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.5.tgz", + "integrity": "sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==", + "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "@jest/get-type": "30.0.1" }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "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==", + "node_modules/@jest/get-type": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", + "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@istanbuljs/load-nyc-config/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/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", "dev": true, "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, "engines": { - "node": ">=8" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/console": { + "node_modules/@jest/schemas": { "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.0.5.tgz", - "integrity": "sha512-xY6b0XiL0Nav3ReresUarwl2oIz1gTnxGbGpho9/rbUWsLH0f1OD/VT84xs8c7VmH7MChnLb0pag6PhZhAdDiA==", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.0.5", - "jest-util": "30.0.5", - "slash": "^3.0.0" + "@sinclair/typebox": "^0.34.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/core": { + "node_modules/@jest/types": { "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.0.5.tgz", - "integrity": "sha512-fKD0OulvRsXF1hmaFgHhVJzczWzA1RXMMo9LTPuFXo9q/alDbME3JIyWYqovWsUBWSoBcsHaGPSLF9rz4l9Qeg==", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.5.tgz", + "integrity": "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.0.5", "@jest/pattern": "30.0.1", - "@jest/reporters": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", - "@jest/types": "30.0.5", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-changed-files": "30.0.5", - "jest-config": "30.0.5", - "jest-haste-map": "30.0.5", - "jest-message-util": "30.0.5", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.5", - "jest-resolve-dependencies": "30.0.5", - "jest-runner": "30.0.5", - "jest-runtime": "30.0.5", - "jest-snapshot": "30.0.5", - "jest-util": "30.0.5", - "jest-validate": "30.0.5", - "jest-watcher": "30.0.5", - "micromatch": "^4.0.8", - "pretty-format": "30.0.5", - "slash": "^3.0.0" + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/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/@jest/core/node_modules/jest-config": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.5.tgz", - "integrity": "sha512-aIVh+JNOOpzUgzUnPn5FLtyVnqc3TQHVMupYtyeURSb//iLColiMIR8TxCIDKyx9ZgjKnXGucuW68hCxgbrwmA==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.0.1", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.0.5", - "@jest/types": "30.0.5", - "babel-jest": "30.0.5", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-circus": "30.0.5", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.5", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.5", - "jest-runner": "30.0.5", - "jest-util": "30.0.5", - "jest-validate": "30.0.5", - "micromatch": "^4.0.8", - "parse-json": "^5.2.0", - "pretty-format": "30.0.5", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "esbuild-register": ">=3.4.0", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jest/core/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=6.0.0" } }, - "node_modules/@jest/core/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==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", "dev": true, "license": "MIT" }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "dev": true, "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jest/environment": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.5.tgz", - "integrity": "sha512-aRX7WoaWx1oaOkDQvCWImVQ8XNtdv5sEWgk4gxR6NXb7WBUnL5sRak4WRzIQRZ1VTWPvV4VI4mgGjNL9TeKMYA==", - "dev": true, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.1.tgz", + "integrity": "sha512-CPle1OQehbWqd25La9Ack5B07StKIxh4+Bf19qnpZKJC1oI22Y0czZHbifjw1UoczIfKBwBDAp/dFxvHG13B5A==", "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-mock": "30.0.5" + "ajv": "^6.12.6", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=18" } }, - "node_modules/@jest/environment-jsdom-abstract": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.0.5.tgz", - "integrity": "sha512-gpWwiVxZunkoglP8DCnT3As9x5O8H6gveAOpvaJd2ATAoSh7ZSSCWbr9LQtUMvr8WD3VjG9YnDhsmkCK5WN1rQ==", + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@jest/environment": "30.0.5", - "@jest/fake-timers": "30.0.5", - "@jest/types": "30.0.5", - "@types/jsdom": "^21.1.7", - "@types/node": "*", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "canvas": "^3.0.0", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" } }, - "node_modules/@jest/expect": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.5.tgz", - "integrity": "sha512-6udac8KKrtTtC+AXZ2iUN/R7dp7Ydry+Fo6FPFnDG54wjVMnb6vW/XNlf7Xj8UDjAE3aAVAsR4KFyKk3TCXmTA==", + "node_modules/@next/env": { + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.4.6.tgz", + "integrity": "sha512-yHDKVTcHrZy/8TWhj0B23ylKv5ypocuCwey9ZqPyv4rPdUdRzpGCkSi03t04KBPyU96kxVtUqx6O3nE1kpxASQ==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.4.6.tgz", + "integrity": "sha512-2NOu3ln+BTcpnbIDuxx6MNq+pRrCyey4WSXGaJIyt0D2TYicHeO9QrUENNjcf673n3B1s7hsiV5xBYRCK1Q8kA==", "dev": true, "license": "MIT", "dependencies": { - "expect": "30.0.5", - "jest-snapshot": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "fast-glob": "3.3.1" } }, - "node_modules/@jest/expect-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.5.tgz", - "integrity": "sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==", + "node_modules/@next/eslint-plugin-next/node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8.6.0" } }, - "node_modules/@jest/fake-timers": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.5.tgz", - "integrity": "sha512-ZO5DHfNV+kgEAeP3gK3XlpJLL4U3Sz6ebl/n68Uwt64qFFs5bv4bfEEjyRGK5uM0C90ewooNgFuKMdkbEoMEXw==", + "node_modules/@next/eslint-plugin-next/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@jest/types": "30.0.5", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.0.5", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" + "is-glob": "^4.0.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 6" } }, - "node_modules/@jest/get-type": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", - "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", - "dev": true, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.4.6.tgz", + "integrity": "sha512-667R0RTP4DwxzmrqTs4Lr5dcEda9OxuZsVFsjVtxVMVhzSpo6nLclXejJVfQo2/g7/Z9qF3ETDmN3h65mTjpTQ==", + "cpu": [ + "arm64" + ], "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 10" } }, - "node_modules/@jest/globals": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.5.tgz", - "integrity": "sha512-7oEJT19WW4oe6HR7oLRvHxwlJk2gev0U9px3ufs8sX9PoD1Eza68KF0/tlN7X0dq/WVsBScXQGgCldA1V9Y/jA==", - "dev": true, + "node_modules/@next/swc-darwin-x64": { + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.4.6.tgz", + "integrity": "sha512-KMSFoistFkaiQYVQQnaU9MPWtp/3m0kn2Xed1Ces5ll+ag1+rlac20sxG+MqhH2qYWX1O2GFOATQXEyxKiIscg==", + "cpu": [ + "x64" + ], "license": "MIT", - "dependencies": { - "@jest/environment": "30.0.5", - "@jest/expect": "30.0.5", - "@jest/types": "30.0.5", - "jest-mock": "30.0.5" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.0.5.tgz", - "integrity": "sha512-mafft7VBX4jzED1FwGC1o/9QUM2xebzavImZMeqnsklgcyxBto8mV4HzNSzUrryJ+8R9MFOM3HgYuDradWR+4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", - "@jest/types": "30.0.5", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.0.5", - "jest-util": "30.0.5", - "jest-worker": "30.0.5", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.5.tgz", - "integrity": "sha512-XcCQ5qWHLvi29UUrowgDFvV4t7ETxX91CbDczMnoqXPOIcZOxyNdSjm6kV5XMc8+HkxfRegU/MUmnTbJRzGrUQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", - "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.0.5.tgz", - "integrity": "sha512-wPyztnK0gbDMQAJZ43tdMro+qblDHH1Ru/ylzUo21TBKqt88ZqnKKK2m30LKmLLoKtR2lxdpCC/P3g1vfKcawQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.0.5", - "@jest/types": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.0.5.tgz", - "integrity": "sha512-Aea/G1egWoIIozmDD7PBXUOxkekXl7ueGzrsGGi1SbeKgQqCYCIf+wfbflEbf2LiPxL8j2JZGLyrzZagjvW4YQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.0.5", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.5.tgz", - "integrity": "sha512-Vk8amLQCmuZyy6GbBht1Jfo9RSdBtg7Lks+B0PecnjI8J+PCLQPGh7uI8Q/2wwpW2gLdiAfiHNsmekKlywULqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.0.5", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.0", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", - "jest-regex-util": "30.0.1", - "jest-util": "30.0.5", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/types": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.5.tgz", - "integrity": "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.0.tgz", - "integrity": "sha512-qFfbWFA7r1Sd8D697L7GkTd36yqDuTkvz0KfOGkgXR8EUhQn3/EDNIR/qUdQNMT8IjmasBvHWuXeisxtXTQT2g==", - "license": "MIT", - "dependencies": { - "ajv": "^6.12.6", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", - "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.10.0" - } - }, - "node_modules/@next/env": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.4.5.tgz", - "integrity": "sha512-ruM+q2SCOVCepUiERoxOmZY9ZVoecR3gcXNwCYZRvQQWRjhOiPJGmQ2fAiLR6YKWXcSAh7G79KEFxN3rwhs4LQ==", - "license": "MIT" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.4.5.tgz", - "integrity": "sha512-YhbrlbEt0m4jJnXHMY/cCUDBAWgd5SaTa5mJjzOt82QwflAFfW/h3+COp2TfVSzhmscIZ5sg2WXt3MLziqCSCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "3.3.1" - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.4.5.tgz", - "integrity": "sha512-84dAN4fkfdC7nX6udDLz9GzQlMUwEMKD7zsseXrl7FTeIItF8vpk1lhLEnsotiiDt+QFu3O1FVWnqwcRD2U3KA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.4.5.tgz", - "integrity": "sha512-CL6mfGsKuFSyQjx36p2ftwMNSb8PQog8y0HO/ONLdQqDql7x3aJb/wB+LA651r4we2pp/Ck+qoRVUeZZEvSurA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" + "node": ">= 10" } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.4.5.tgz", - "integrity": "sha512-1hTVd9n6jpM/thnDc5kYHD1OjjWYpUJrJxY4DlEacT7L5SEOXIifIdTye6SQNNn8JDZrcN+n8AWOmeJ8u3KlvQ==", + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.4.6.tgz", + "integrity": "sha512-PnOx1YdO0W7m/HWFeYd2A6JtBO8O8Eb9h6nfJia2Dw1sRHoHpNf6lN1U4GKFRzRDBi9Nq2GrHk9PF3Vmwf7XVw==", "cpu": [ "arm64" ], @@ -2394,9 +1990,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.4.5.tgz", - "integrity": "sha512-4W+D/nw3RpIwGrqpFi7greZ0hjrCaioGErI7XHgkcTeWdZd146NNu1s4HnaHonLeNTguKnL2Urqvj28UJj6Gqw==", + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.4.6.tgz", + "integrity": "sha512-XBbuQddtY1p5FGPc2naMO0kqs4YYtLYK/8aPausI5lyOjr4J77KTG9mtlU4P3NwkLI1+OjsPzKVvSJdMs3cFaw==", "cpu": [ "arm64" ], @@ -2410,9 +2006,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.4.5.tgz", - "integrity": "sha512-N6Mgdxe/Cn2K1yMHge6pclffkxzbSGOydXVKYOjYqQXZYjLCfN/CuFkaYDeDHY2VBwSHyM2fUjYBiQCIlxIKDA==", + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.4.6.tgz", + "integrity": "sha512-+WTeK7Qdw82ez3U9JgD+igBAP75gqZ1vbK6R8PlEEuY0OIe5FuYXA4aTjL811kWPf7hNeslD4hHK2WoM9W0IgA==", "cpu": [ "x64" ], @@ -2426,9 +2022,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.4.5.tgz", - "integrity": "sha512-YZ3bNDrS8v5KiqgWE0xZQgtXgCTUacgFtnEgI4ccotAASwSvcMPDLua7BWLuTfucoRv6mPidXkITJLd8IdJplQ==", + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.4.6.tgz", + "integrity": "sha512-XP824mCbgQsK20jlXKrUpZoh/iO3vUWhMpxCz8oYeagoiZ4V0TQiKy0ASji1KK6IAe3DYGfj5RfKP6+L2020OQ==", "cpu": [ "x64" ], @@ -2442,9 +2038,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.4.5.tgz", - "integrity": "sha512-9Wr4t9GkZmMNcTVvSloFtjzbH4vtT4a8+UHqDoVnxA5QyfWe6c5flTH1BIWPGNWSUlofc8dVJAE7j84FQgskvQ==", + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.4.6.tgz", + "integrity": "sha512-FxrsenhUz0LbgRkNWx6FRRJIPe/MI1JRA4W4EPd5leXO00AZ6YU8v5vfx4MDXTvN77lM/EqsE3+6d2CIeF5NYg==", "cpu": [ "arm64" ], @@ -2458,9 +2054,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.4.5.tgz", - "integrity": "sha512-voWk7XtGvlsP+w8VBz7lqp8Y+dYw/MTI4KeS0gTVtfdhdJ5QwhXLmNrndFOin/MDoCvUaLWMkYKATaCoUkt2/A==", + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.4.6.tgz", + "integrity": "sha512-T4ufqnZ4u88ZheczkBTtOF+eKaM14V8kbjud/XrAakoM5DKQWjW09vD6B9fsdsWS2T7D5EY31hRHdta7QKWOng==", "cpu": [ "x64" ], @@ -2558,59 +2154,320 @@ "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/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", - "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", "dev": true, "license": "MIT" }, - "node_modules/@sinclair/typebox": { - "version": "0.34.38", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz", - "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==", + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.30", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.30.tgz", + "integrity": "sha512-whXaSoNUFiyDAjkUF8OBpOm77Szdbk5lGNqFe6CbVbJFrhCCPinCbRA3NjawwlNHla1No7xvXXh+CpSxnPfUEw==", "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==", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "cpu": [ + "arm" + ], "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", + "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.38", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz", + "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==", + "dev": true, + "license": "MIT" }, "node_modules/@swc/helpers": { "version": "0.5.15", @@ -2883,117 +2740,39 @@ "node": ">= 10" } }, - "node_modules/@tailwindcss/oxide/node_modules/chownr": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", - "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "node_modules/@tailwindcss/postcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz", + "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==", "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", + "postcss": "^8.4.41", + "tailwindcss": "4.1.11" } }, - "node_modules/@tailwindcss/oxide/node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "minipass": "^7.1.2" + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" }, "engines": { - "node": ">= 18" - } - }, - "node_modules/@tailwindcss/oxide/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@tailwindcss/oxide/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dev": true, - "license": "ISC", - "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", - "yallist": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@tailwindcss/oxide/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@tailwindcss/postcss": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz", - "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.11", - "@tailwindcss/oxide": "4.1.11", - "postcss": "^8.4.41", - "tailwindcss": "4.1.11" - } - }, - "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "dequal": "^2.0.3" + "node": ">=18" } }, "node_modules/@testing-library/jest-dom": { @@ -3143,15 +2922,32 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "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/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.20.7" + "@types/deep-eql": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -3232,18 +3028,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/jsdom": { - "version": "21.1.7", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", - "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^7.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", @@ -3259,13 +3043,13 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz", - "integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==", + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz", + "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.8.0" + "undici-types": "~7.10.0" } }, "node_modules/@types/react": { @@ -3305,13 +3089,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -3330,17 +3107,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", - "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz", + "integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/type-utils": "8.38.0", - "@typescript-eslint/utils": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/type-utils": "8.39.0", + "@typescript-eslint/utils": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3354,9 +3131,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.38.0", + "@typescript-eslint/parser": "^8.39.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -3370,16 +3147,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", - "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz", + "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", "debug": "^4.3.4" }, "engines": { @@ -3391,18 +3168,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", - "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz", + "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.38.0", - "@typescript-eslint/types": "^8.38.0", + "@typescript-eslint/tsconfig-utils": "^8.39.0", + "@typescript-eslint/types": "^8.39.0", "debug": "^4.3.4" }, "engines": { @@ -3413,18 +3190,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", - "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz", + "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0" + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3435,9 +3212,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", - "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz", + "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==", "dev": true, "license": "MIT", "engines": { @@ -3448,19 +3225,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", - "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz", + "integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0", - "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3473,13 +3250,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", - "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz", + "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==", "dev": true, "license": "MIT", "engines": { @@ -3491,16 +3268,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", - "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz", + "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.38.0", - "@typescript-eslint/tsconfig-utils": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/visitor-keys": "8.38.0", + "@typescript-eslint/project-service": "8.39.0", + "@typescript-eslint/tsconfig-utils": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/visitor-keys": "8.39.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3516,7 +3293,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { @@ -3529,36 +3306,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -3576,16 +3323,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", - "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz", + "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.38.0", - "@typescript-eslint/types": "8.38.0", - "@typescript-eslint/typescript-estree": "8.38.0" + "@typescript-eslint/scope-manager": "8.39.0", + "@typescript-eslint/types": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3596,17 +3343,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", - "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz", + "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/types": "8.39.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3617,13 +3364,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, "node_modules/@unrs/resolver-binding-android-arm-eabi": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", @@ -3893,6 +3633,198 @@ "win32" ] }, + "node_modules/@vitejs/plugin-react": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.0.tgz", + "integrity": "sha512-Jx9JfsTa05bYkS9xo0hkofp2dCmp1blrKjw9JONs5BTHOvJCgLbaPSuZLGSVJW6u2qe0tc4eevY0+gSNNi0YCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.30", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-3.2.4.tgz", + "integrity": "sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^1.0.2", + "ast-v8-to-istanbul": "^0.3.3", + "debug": "^4.4.1", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.17", + "magicast": "^0.3.5", + "std-env": "^3.9.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "3.2.4", + "vitest": "3.2.4" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-3.2.4.tgz", + "integrity": "sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "fflate": "^0.8.2", + "flatted": "^3.3.3", + "pathe": "^2.0.3", + "sirv": "^3.0.1", + "tinyglobby": "^0.2.14", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "3.2.4" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -3913,15 +3845,6 @@ "node": ">= 0.6" } }, - "node_modules/accepts/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/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -3946,16 +3869,13 @@ } }, "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==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "debug": "4" - }, "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/agentkeepalive": { @@ -4001,22 +3921,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "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", @@ -4043,20 +3947,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/aproba": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", @@ -4087,13 +3977,13 @@ "license": "Python-2.0" }, "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" + "dependencies": { + "dequal": "^2.0.3" } }, "node_modules/array-buffer-byte-length": { @@ -4256,6 +4146,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -4263,10 +4163,22 @@ "dev": true, "license": "MIT" }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "node_modules/ast-v8-to-istanbul": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-0.3.4.tgz", + "integrity": "sha512-cxrAnZNLBnQwBPByK4CeDaw5sWZtMilJE/Q3iDA0aamgaIVNDF9T6K2/8DfYDZEejZ2jNnDrG9m8MY72HFd0KA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.29", + "estree-walker": "^3.0.3", + "js-tokens": "^9.0.1" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", "dev": true, "license": "MIT" }, @@ -4316,104 +4228,6 @@ "node": ">= 0.4" } }, - "node_modules/babel-jest": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.5.tgz", - "integrity": "sha512-mRijnKimhGDMsizTvBTWotwNpzrkHr+VvZUQBof2AufXKB8NXrL1W69TG20EvOz7aevx6FTJIaBuBkYxS8zolg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "30.0.5", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.0", - "babel-preset-jest": "30.0.1", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", - "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.1.tgz", - "integrity": "sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "@types/babel__core": "^7.20.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.1.tgz", - "integrity": "sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "30.0.1", - "babel-preset-current-node-syntax": "^1.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4538,29 +4352,6 @@ "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", @@ -4585,13 +4376,6 @@ "ieee754": "^1.1.13" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -4601,6 +4385,16 @@ "node": ">= 0.8" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/cacache": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", @@ -4631,28 +4425,6 @@ "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", @@ -4666,19 +4438,41 @@ "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==", + "node_modules/cacache/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==", "license": "ISC", "optional": true, "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/cacache/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", + "optional": true, "engines": { "node": ">=8" } }, + "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", @@ -4737,20 +4531,10 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/caniuse-lite": { - "version": "1.0.30001727", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", - "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "funding": [ { "type": "opencollective", @@ -4767,6 +4551,23 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chai": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", + "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4784,14 +4585,14 @@ "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==", + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 16" } }, "node_modules/chownr": { @@ -4819,13 +4620,6 @@ "node": ">=8" } }, - "node_modules/cjs-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", - "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", - "dev": true, - "license": "MIT" - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -4842,74 +4636,6 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -4919,24 +4645,6 @@ "node": ">=6" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true, - "license": "MIT" - }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -5231,19 +4939,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", "dev": true, "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } + "engines": { + "node": ">=6" } }, "node_modules/deep-extend": { @@ -5262,16 +4965,6 @@ "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/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -5330,7 +5023,6 @@ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -5344,16 +5036,6 @@ "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/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -5402,42 +5084,13 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/electron-to-chromium": { - "version": "1.5.190", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.190.tgz", - "integrity": "sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw==", + "version": "1.5.198", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.198.tgz", + "integrity": "sha512-G5COfnp3w+ydVu80yprgWSfmfQaYRh9DOxfhAxstLyetKaLyl55QrNjx8C38Pc/C+RaDmb1M0Lk8wPEMQ+bGgQ==", "dev": true, "license": "ISC" }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -5474,9 +5127,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", - "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", "dev": true, "license": "MIT", "dependencies": { @@ -5517,23 +5170,6 @@ "license": "MIT", "optional": true }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/error-ex/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/es-abstract": { "version": "1.24.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", @@ -5649,6 +5285,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -5708,6 +5351,48 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -5799,13 +5484,13 @@ } }, "node_modules/eslint-config-next": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.4.5.tgz", - "integrity": "sha512-IMijiXaZ43qFB+Gcpnb374ipTKD8JIyVNR+6VsifFQ/LHyx+A9wgcgSIhCX5PYSjwOoSYD5LtNHKlM5uc23eww==", + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.4.6.tgz", + "integrity": "sha512-4uznvw5DlTTjrZgYZjMciSdDDMO2SWIuQgUNaFyC2O3Zw3Z91XeIejeVa439yRq2CnJb/KEvE4U2AeN/66FpUA==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.4.5", + "@next/eslint-plugin-next": "15.4.6", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", @@ -5995,6 +5680,16 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eslint-plugin-react": { "version": "7.37.5", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", @@ -6117,20 +5812,6 @@ "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.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -6167,6 +5848,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -6207,47 +5898,6 @@ "node": ">=20.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-x": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", - "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -6275,6 +5925,16 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/express": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", @@ -6339,9 +5999,9 @@ "license": "MIT" }, "node_modules/fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, "license": "MIT", "dependencies": { @@ -6349,7 +6009,7 @@ "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" @@ -6391,14 +6051,19 @@ "reusify": "^1.0.4" } }, - "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==", + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, "node_modules/fetch-blob": { @@ -6425,6 +6090,13 @@ "node": "^12.20 || >= 14.13" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -6444,39 +6116,6 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", "license": "MIT" }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/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/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -6591,6 +6230,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -6640,24 +6292,12 @@ "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.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" + "license": "ISC", + "optional": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -6735,48 +6375,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/gauge/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", - "optional": true - }, - "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/gauge/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", - "optional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/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", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -6787,16 +6385,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -6821,16 +6409,6 @@ "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", @@ -6844,19 +6422,6 @@ "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/get-symbol-description": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", @@ -6895,21 +6460,22 @@ "license": "MIT" }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, + "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": { - "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" + "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" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "engines": { + "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -6928,32 +6494,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/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/glob/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/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -7152,43 +6692,41 @@ "node": ">= 0.8" } }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/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==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "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==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "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": ">= 14" } }, "node_modules/humanize-ms": { @@ -7260,26 +6798,6 @@ "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", @@ -7312,8 +6830,8 @@ "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", + "optional": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -7405,1327 +6923,526 @@ "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bun-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", - "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.7.1" - } - }, - "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==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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-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-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, - "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-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "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": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.5.tgz", - "integrity": "sha512-y2mfcJywuTUkvLm2Lp1/pFX8kTgMO5yyQGq/Sk/n2mN7XWYp4JsCZ/QXW34M8YScgk8bPZlREH04f6blPnoHnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "30.0.5", - "@jest/types": "30.0.5", - "import-local": "^3.2.0", - "jest-cli": "30.0.5" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.0.5.tgz", - "integrity": "sha512-bGl2Ntdx0eAwXuGpdLdVYVr5YQHnSZlQ0y9HVDu565lCUAe9sj6JOtBbMmBBikGIegne9piDDIOeiLVoqTkz4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.1.1", - "jest-util": "30.0.5", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-circus": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.0.5.tgz", - "integrity": "sha512-h/sjXEs4GS+NFFfqBDYT7y5Msfxh04EwWLhQi0F8kuWpe+J/7tICSlswU8qvBqumR3kFgHbfu7vU6qruWWBPug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.0.5", - "@jest/expect": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.0.5", - "jest-matcher-utils": "30.0.5", - "jest-message-util": "30.0.5", - "jest-runtime": "30.0.5", - "jest-snapshot": "30.0.5", - "jest-util": "30.0.5", - "p-limit": "^3.1.0", - "pretty-format": "30.0.5", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-circus/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/jest-circus/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-circus/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/jest-cli": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.0.5.tgz", - "integrity": "sha512-Sa45PGMkBZzF94HMrlX4kUyPOwUpdZasaliKN3mifvDmkhLYqLLg8HQTzn6gq7vJGahFYMQjXgyJWfYImKZzOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/types": "30.0.5", - "chalk": "^4.1.2", - "exit-x": "^0.2.2", - "import-local": "^3.2.0", - "jest-config": "30.0.5", - "jest-util": "30.0.5", - "jest-validate": "30.0.5", - "yargs": "^17.7.2" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/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", + "safe-regex-test": "^1.1.0" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli/node_modules/jest-config": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.5.tgz", - "integrity": "sha512-aIVh+JNOOpzUgzUnPn5FLtyVnqc3TQHVMupYtyeURSb//iLColiMIR8TxCIDKyx9ZgjKnXGucuW68hCxgbrwmA==", + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.0.1", - "@jest/pattern": "30.0.1", - "@jest/test-sequencer": "30.0.5", - "@jest/types": "30.0.5", - "babel-jest": "30.0.5", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-circus": "30.0.5", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.5", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.5", - "jest-runner": "30.0.5", - "jest-util": "30.0.5", - "jest-validate": "30.0.5", - "micromatch": "^4.0.8", - "parse-json": "^5.2.0", - "pretty-format": "30.0.5", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "has-bigints": "^1.0.2" }, "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" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-cli/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/jest-diff": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.5.tgz", - "integrity": "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==", + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.0.1", - "chalk": "^4.1.2", - "pretty-format": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "semver": "^7.7.1" } }, - "node_modules/jest-diff/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==", + "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==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "hasown": "^2.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-diff/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/jest-docblock": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.0.1.tgz", - "integrity": "sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==", + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, "license": "MIT", "dependencies": { - "detect-newline": "^3.1.0" + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.0.5.tgz", - "integrity": "sha512-dKjRsx1uZ96TVyejD3/aAWcNKy6ajMaN531CwWIsrazIqIoXI9TnnpPlkrEYku/8rkS3dh2rbH+kMOyiEIv0xQ==", + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1", - "@jest/types": "30.0.5", - "chalk": "^4.1.2", - "jest-util": "30.0.5", - "pretty-format": "30.0.5" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/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==", + "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": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/jest-each/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "call-bound": "^1.0.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each/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/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==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/jest-environment-jsdom": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-30.0.5.tgz", - "integrity": "sha512-BmnDEoAH+jEjkPrvE9DTKS2r3jYSJWlN/r46h0/DBUxKrkgt2jAZ5Nj4wXLAcV1KWkRpcFqA5zri9SWzJZ1cCg==", + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.0.5", - "@jest/environment-jsdom-abstract": "30.0.5", - "@types/jsdom": "^21.1.7", - "@types/node": "*", - "jsdom": "^26.1.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "canvas": "^3.0.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-environment-node": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.5.tgz", - "integrity": "sha512-ppYizXdLMSvciGsRsMEnv/5EFpvOdXBaXRBzFUDPWrsfmog4kYrOGWXarLllz6AXan6ZAA/kYokgDWuos1IKDA==", + "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": { - "@jest/environment": "30.0.5", - "@jest/fake-timers": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-mock": "30.0.5", - "jest-util": "30.0.5", - "jest-validate": "30.0.5" + "is-extglob": "^2.1.1" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-haste-map": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.5.tgz", - "integrity": "sha512-dkmlWNlsTSR0nH3nRfW5BKbqHefLZv0/6LCccG0xFCTWcJu8TuEwG+5Cm75iBfjVoockmO6J35o5gxtFSn5xeg==", + "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-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.1", - "jest-util": "30.0.5", - "jest-worker": "30.0.5", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" }, - "optionalDependencies": { - "fsevents": "^2.3.3" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-leak-detector": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.0.5.tgz", - "integrity": "sha512-3Uxr5uP8jmHMcsOtYMRB/zf1gXN3yUIc+iPorhNETG54gErFIiUhLvyY/OggYpSMOEYqsmRxmuU4ZOoX5jpRFg==", + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/get-type": "30.0.1", - "pretty-format": "30.0.5" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } - }, - "node_modules/jest-leak-detector/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==", + }, + "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": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.12.0" } }, - "node_modules/jest-leak-detector/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-leak-detector/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==", + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "dev": true, "license": "MIT" }, - "node_modules/jest-matcher-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.5.tgz", - "integrity": "sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==", + "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-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1", - "chalk": "^4.1.2", - "jest-diff": "30.0.5", - "pretty-format": "30.0.5" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-matcher-utils/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==", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "call-bound": "^1.0.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-matcher-utils/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/jest-message-util": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.5.tgz", - "integrity": "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.0.5", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.0.5", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-message-util/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==", + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "which-typed-array": "^1.1.16" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-message-util/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==", + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/jest-mock": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.5.tgz", - "integrity": "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==", + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-util": "30.0.5" + "call-bound": "^1.0.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, - "peerDependencies": { - "jest-resolve": "*" + "engines": { + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true, - "license": "MIT", + "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": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-resolve": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.0.5.tgz", - "integrity": "sha512-d+DjBQ1tIhdz91B79mywH5yYu76bZuE96sSbxj8MkjWVx5WNdt1deEFRONVL4UkKLSrAbMkdhb24XN691yDRHg==", + "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": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.0.5", - "jest-validate": "30.0.5", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" } }, - "node_modules/jest-resolve-dependencies": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.5.tgz", - "integrity": "sha512-/xMvBR4MpwkrHW4ikZIWRttBBRZgWK4d6xt3xW1iRDSKt4tXzYkMkyPfBnSCgv96cpkrctfXs6gexeqMYqdEpw==", + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "jest-regex-util": "30.0.1", - "jest-snapshot": "30.0.5" + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" } }, - "node_modules/jest-runner": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.0.5.tgz", - "integrity": "sha512-JcCOucZmgp+YuGgLAXHNy7ualBx4wYSgJVWrYMRBnb79j9PD0Jxh0EHvR5Cx/r0Ce+ZBC4hCdz2AzFFLl9hCiw==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@jest/console": "30.0.5", - "@jest/environment": "30.0.5", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-docblock": "30.0.1", - "jest-environment-node": "30.0.5", - "jest-haste-map": "30.0.5", - "jest-leak-detector": "30.0.5", - "jest-message-util": "30.0.5", - "jest-resolve": "30.0.5", - "jest-runtime": "30.0.5", - "jest-util": "30.0.5", - "jest-watcher": "30.0.5", - "jest-worker": "30.0.5", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-runtime": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.0.5.tgz", - "integrity": "sha512-7oySNDkqpe4xpX5PPiJTe5vEa+Ak/NnNz2bGYZrA1ftG3RL3EFlHaUkA1Cjx+R8IhK0Vg43RML5mJedGTPNz3A==", + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.0.5", - "@jest/fake-timers": "30.0.5", - "@jest/globals": "30.0.5", - "@jest/source-map": "30.0.1", - "@jest/test-result": "30.0.5", - "@jest/transform": "30.0.5", - "@jest/types": "30.0.5", - "@types/node": "*", - "chalk": "^4.1.2", - "cjs-module-lexer": "^2.1.0", - "collect-v8-coverage": "^1.0.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.5", - "jest-message-util": "30.0.5", - "jest-mock": "30.0.5", - "jest-regex-util": "30.0.1", - "jest-resolve": "30.0.5", - "jest-snapshot": "30.0.5", - "jest-util": "30.0.5", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-runtime/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==", + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "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-snapshot": { + "node_modules/jest-diff": { "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.5.tgz", - "integrity": "sha512-T00dWU/Ek3LqTp4+DcW6PraVxjk28WY5Ua/s+3zUKSERZSNyxTqhDXCWKG5p2HAJ+crVQ3WJ2P9YVHpj1tkW+g==", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.5.tgz", + "integrity": "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.0.5", + "@jest/diff-sequences": "30.0.1", "@jest/get-type": "30.0.1", - "@jest/snapshot-utils": "30.0.5", - "@jest/transform": "30.0.5", - "@jest/types": "30.0.5", - "babel-preset-current-node-syntax": "^1.1.0", "chalk": "^4.1.2", - "expect": "30.0.5", - "graceful-fs": "^4.2.11", - "jest-diff": "30.0.5", - "jest-matcher-utils": "30.0.5", - "jest-message-util": "30.0.5", - "jest-util": "30.0.5", - "pretty-format": "30.0.5", - "semver": "^7.7.2", - "synckit": "^0.11.8" + "pretty-format": "30.0.5" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { + "node_modules/jest-diff/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==", @@ -8738,7 +7455,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-snapshot/node_modules/pretty-format": { + "node_modules/jest-diff/node_modules/pretty-format": { "version": "30.0.5", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", @@ -8753,63 +7470,86 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/react-is": { + "node_modules/jest-diff/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/jest-util": { + "node_modules/jest-matcher-utils": { "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.5.tgz", - "integrity": "sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.5.tgz", + "integrity": "sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", + "@jest/get-type": "30.0.1", "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "jest-diff": "30.0.5", + "pretty-format": "30.0.5" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/jest-matcher-utils/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": ">=12" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", + "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-validate": { + "node_modules/jest-matcher-utils/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/jest-message-util": { "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.0.5.tgz", - "integrity": "sha512-ouTm6VFHaS2boyl+k4u+Qip4TSH7Uld5tyD8psQ8abGgt2uYYB8VwVfAHWHjHc0NWmGGbwO5h0sCPOGHHevefw==", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.5.tgz", + "integrity": "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1", + "@babel/code-frame": "^7.27.1", "@jest/types": "30.0.5", - "camelcase": "^6.3.0", + "@types/stack-utils": "^2.0.3", "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.0.5" + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.0.5", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-validate/node_modules/ansi-styles": { + "node_modules/jest-message-util/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==", @@ -8822,20 +7562,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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-validate/node_modules/pretty-format": { + "node_modules/jest-message-util/node_modules/pretty-format": { "version": "30.0.5", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", @@ -8850,64 +7577,54 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-validate/node_modules/react-is": { + "node_modules/jest-message-util/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/jest-watcher": { + "node_modules/jest-mock": { "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.0.5.tgz", - "integrity": "sha512-z9slj/0vOwBDBjN3L4z4ZYaA+pG56d6p3kTUhFRYGvXbXMWhXmb/FIxREZCD06DYUwDKKnj2T80+Pb71CQ0KEg==", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.5.tgz", + "integrity": "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "30.0.5", "@jest/types": "30.0.5", "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.0.5", - "string-length": "^4.0.2" + "jest-util": "30.0.5" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-worker": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.5.tgz", - "integrity": "sha512-ojRXsWzEP16NdUuBw/4H/zkZdHOa7MMYCk4E430l+8fELeLg/mqmMlRhjL7UNZvQrDmnovWZV4DxX03fZF48fQ==", + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.0.5", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/jest-util": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.5.tgz", + "integrity": "sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jiti": { @@ -8987,44 +7704,6 @@ } } }, - "node_modules/jsdom/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/jsdom/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/jsdom/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -9045,13 +7724,6 @@ "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", @@ -9066,16 +7738,16 @@ "license": "MIT" }, "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "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", - "dependencies": { - "minimist": "^1.2.0" - }, "bin": { "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, "node_modules/jsx-ast-utils": { @@ -9124,16 +7796,6 @@ "node": ">=0.10" } }, - "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", @@ -9190,6 +7852,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9211,6 +7874,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9232,6 +7896,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9253,6 +7918,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9274,6 +7940,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9295,6 +7962,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9316,6 +7984,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9337,6 +8006,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9358,6 +8028,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9379,6 +8050,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9387,13 +8059,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -9417,13 +8082,6 @@ "dev": true, "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", @@ -9444,6 +8102,13 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -9452,14 +8117,7 @@ "license": "ISC", "dependencies": { "yallist": "^3.0.2" - } - }, - "node_modules/lru-cache/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/lz-string": { "version": "1.5.0", @@ -9482,6 +8140,18 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -9498,13 +8168,6 @@ "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==", - "dev": 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", @@ -9533,6 +8196,48 @@ "node": ">= 10" } }, + "node_modules/make-fetch-happen/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/make-fetch-happen/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/make-fetch-happen/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/make-fetch-happen/node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -9546,28 +8251,22 @@ "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", + "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, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "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/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/math-intrinsics": { "version": "1.1.0", @@ -9599,13 +8298,6 @@ "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/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -9630,6 +8322,19 @@ "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-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -9651,16 +8356,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -9706,13 +8401,15 @@ } }, "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, + "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": ">=16 || 14 >=14.17" + "node": ">=8" } }, "node_modules/minipass-collect": { @@ -9728,19 +8425,6 @@ "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-fetch": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", @@ -9759,19 +8443,6 @@ "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-flush": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", @@ -9785,19 +8456,6 @@ "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-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", @@ -9811,19 +8469,6 @@ "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-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", @@ -9837,18 +8482,11 @@ "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/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/minizlib": { "version": "2.1.2", @@ -9863,17 +8501,11 @@ "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": "1.0.4", @@ -9893,6 +8525,16 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", "license": "MIT" }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9947,22 +8589,21 @@ "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", - "optional": true, "engines": { "node": ">= 0.6" } }, "node_modules/next": { - "version": "15.4.5", - "resolved": "https://registry.npmjs.org/next/-/next-15.4.5.tgz", - "integrity": "sha512-nJ4v+IO9CPmbmcvsPebIoX3Q+S7f6Fu08/dEWu0Ttfa+wVwQRh9epcmsyCPjmL2b8MxC+CkBR97jgDhUUztI3g==", + "version": "15.4.6", + "resolved": "https://registry.npmjs.org/next/-/next-15.4.6.tgz", + "integrity": "sha512-us++E/Q80/8+UekzB3SAGs71AlLDsadpFMXVNM/uQ0BMwsh9m3mr0UNQIfjKed8vpWXsASe+Qifrnu1oLIcKEQ==", "license": "MIT", "dependencies": { - "@next/env": "15.4.5", + "@next/env": "15.4.6", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -9975,14 +8616,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.4.5", - "@next/swc-darwin-x64": "15.4.5", - "@next/swc-linux-arm64-gnu": "15.4.5", - "@next/swc-linux-arm64-musl": "15.4.5", - "@next/swc-linux-x64-gnu": "15.4.5", - "@next/swc-linux-x64-musl": "15.4.5", - "@next/swc-win32-arm64-msvc": "15.4.5", - "@next/swc-win32-x64-msvc": "15.4.5", + "@next/swc-darwin-arm64": "15.4.6", + "@next/swc-darwin-x64": "15.4.6", + "@next/swc-linux-arm64-gnu": "15.4.6", + "@next/swc-linux-arm64-musl": "15.4.6", + "@next/swc-linux-x64-gnu": "15.4.6", + "@next/swc-linux-x64-musl": "15.4.6", + "@next/swc-win32-arm64-msvc": "15.4.6", + "@next/swc-win32-x64-msvc": "15.4.6", "sharp": "^0.34.3" }, "peerDependencies": { @@ -10119,34 +8760,40 @@ "node": ">= 10.12.0" } }, - "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", + "node_modules/node-gyp/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-gyp/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==", "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" + "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": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10" } }, - "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-gyp/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/node-releases": { "version": "2.0.19", @@ -10164,34 +8811,11 @@ "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" + "bin": { + "nopt": "bin/nopt.js" }, "engines": { - "node": ">=8" + "node": ">=6" } }, "node_modules/npmlog": { @@ -10212,9 +8836,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", + "version": "2.2.21", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz", + "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==", "dev": true, "license": "MIT" }, @@ -10360,22 +8984,6 @@ "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", @@ -10460,16 +9068,6 @@ "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", @@ -10490,25 +9088,6 @@ "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/parse5": { "version": "7.3.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", @@ -10545,8 +9124,8 @@ "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", + "optional": true, "engines": { "node": ">=0.10.0" } @@ -10591,6 +9170,16 @@ "dev": true, "license": "ISC" }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/path-to-regexp": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", @@ -10600,6 +9189,23 @@ "node": ">=16" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -10607,28 +9213,18 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "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": ">=8.6" + "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/pkce-challenge": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", @@ -10638,75 +9234,6 @@ "node": ">=16.20.0" } }, - "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/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", @@ -10812,14 +9339,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -10853,6 +9372,13 @@ "react-is": "^16.13.1" } }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -10885,23 +9411,6 @@ "node": ">=6" } }, - "node_modules/pure-rand": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", - "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, "node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", @@ -11008,11 +9517,22 @@ } }, "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, "node_modules/readable-stream": { "version": "3.6.2", @@ -11086,16 +9606,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -11117,29 +9627,6 @@ "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", @@ -11198,26 +9685,44 @@ "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, + "node_modules/rollup": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", + "dev": true, + "license": "MIT", "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" + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": "*" + "node": ">=18.0.0", + "npm": ">=8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", + "fsevents": "~2.3.2" } }, "node_modules/router": { @@ -11614,18 +10119,19 @@ "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==", + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, + "license": "ISC" + }, + "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", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "optional": true }, "node_modules/simple-concat": { "version": "1.0.1", @@ -11682,6 +10188,21 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/sirv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz", + "integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -11733,14 +10254,17 @@ "node": ">= 10" } }, - "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", + "node_modules/socks-proxy-agent/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": ">=0.10.0" + "node": ">= 6.0.0" } }, "node_modules/source-map-js": { @@ -11752,17 +10276,6 @@ "node": ">=0.10.0" } }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", @@ -11788,12 +10301,44 @@ "peerDependencies": { "node-gyp": "8.x" }, - "peerDependenciesMeta": { - "node-gyp": { - "optional": true - } + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/sqlite3/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/sqlite3/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==", + "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/sqlite3/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/ssri": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", @@ -11807,19 +10352,6 @@ "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/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", @@ -11850,15 +10382,29 @@ "node": ">=8" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "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/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -11882,49 +10428,19 @@ "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-length/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "devOptional": true, "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/string-width-cjs": { @@ -11950,18 +10466,12 @@ "dev": true, "license": "MIT" }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "node_modules/string-width/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==", + "devOptional": true, + "license": "MIT" }, "node_modules/string.prototype.includes": { "version": "2.0.1", @@ -12077,19 +10587,16 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "devOptional": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/strip-ansi-cjs": { @@ -12106,19 +10613,6 @@ "node": ">=8" } }, - "node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -12129,16 +10623,6 @@ "node": ">=4" } }, - "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-indent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", @@ -12165,6 +10649,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -12221,22 +10725,6 @@ "dev": true, "license": "MIT" }, - "node_modules/synckit": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", - "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, "node_modules/tailwind-merge": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", @@ -12265,20 +10753,21 @@ } }, "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==", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, "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" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/tar-fs": { @@ -12315,52 +10804,151 @@ "node": ">=6" } }, + "node_modules/tar/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, "license": "ISC", "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tar/node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" } }, "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==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/test-exclude/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/test-exclude/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "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" }, - "engines": { - "node": ">=8" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/test-exclude/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": { - "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" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/test-exclude/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.14", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", @@ -12378,32 +10966,34 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "engines": { + "node": "^18.0.0 || >=20.0.0" } }, - "node_modules/tinyglobby/node_modules/picomatch": { + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=14.0.0" } }, "node_modules/tldts": { @@ -12426,13 +11016,6 @@ "dev": true, "license": "MIT" }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -12455,6 +11038,16 @@ "node": ">=0.6" } }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/tough-cookie": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", @@ -12494,85 +11087,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/ts-jest": { - "version": "29.4.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", - "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/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/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/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -12586,6 +11100,19 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -12617,29 +11144,6 @@ "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", @@ -12733,9 +11237,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "dev": true, "license": "Apache-2.0", "bin": { @@ -12746,6 +11250,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.39.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.0.tgz", + "integrity": "sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.39.0", + "@typescript-eslint/parser": "8.39.0", + "@typescript-eslint/typescript-estree": "8.39.0", + "@typescript-eslint/utils": "8.39.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/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -12766,9 +11294,9 @@ } }, "node_modules/undici": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", - "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.13.0.tgz", + "integrity": "sha512-l+zSMssRqrzDcb3fjMkjjLGmuiiK2pMIcV++mJaAc9vhjSGpvM7h43QgP+OAMb1GImHmbPyG2tBXeuyG5iY4gA==", "dev": true, "license": "MIT", "engines": { @@ -12776,9 +11304,9 @@ } }, "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "dev": true, "license": "MIT" }, @@ -12856,64 +11384,220 @@ "type": "opencollective", "url": "https://opencollective.com/browserslist" }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/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==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "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/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/vite": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.0.tgz", + "integrity": "sha512-3jdAy3NhBJYsa/lCFcnRfbK4kNkO/bhijFCnv5ByUQk/eekYagoV2yQSISUrhpV+5JiY5hmwOh7jNnQ68dFMuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } - ], - "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==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" } }, - "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/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==", + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" }, "engines": { - "node": ">=10.12.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "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", + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, "engines": { - "node": ">= 0.8" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, "node_modules/w3c-xmlserializer": { @@ -12929,16 +11613,6 @@ "node": ">=18" } }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", @@ -13100,49 +11774,31 @@ "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/wide-align/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", - "optional": true - }, - "node_modules/wide-align/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==", + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" }, "engines": { "node": ">=8" } }, - "node_modules/wide-align/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", + "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": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "string-width": "^1.0.2 || 2 || 3 || 4" } }, "node_modules/word-wrap": { @@ -13192,52 +11848,64 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/wrappy": { @@ -13246,20 +11914,6 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", @@ -13299,85 +11953,12 @@ "dev": true, "license": "MIT" }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "license": "ISC" }, "node_modules/yocto-queue": { "version": "0.1.0", @@ -13418,11 +11999,16 @@ "sqlite3": "^5.1.7" }, "devDependencies": { + "@eslint/js": "^9.32.0", + "@jest/types": "30.0.5", "@types/jest": "^30.0.0", + "@vitest/coverage-v8": "^3.2.4", + "@vitest/ui": "^3.2.4", + "eslint": "^9.32.0", "fix-esm-import-path": "^1.10.3", - "jest": "^30.0.5", - "ts-jest": "^29.4.0", - "typescript": "^5.5.0" + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0", + "vitest": "^3.2.4" } }, "web": { @@ -13432,32 +12018,35 @@ "@turn-based-mcp/shared": "file:../shared", "@types/sqlite3": "^5.1.0", "clsx": "^2.1.0", - "next": "^15.4.5", + "next": "^15.4.6", "react": "^19.1.1", "react-dom": "^19.1.1", "sqlite3": "^5.1.7", "tailwind-merge": "^3.3.1" }, "devDependencies": { - "@edge-runtime/jest-environment": "^4.0.0", + "@eslint/js": "^9.32.0", "@tailwindcss/postcss": "^4.1.0", "@testing-library/jest-dom": "^6.6.4", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", - "@types/node": "^24.1.0", + "@types/node": "^24.2.0", "@types/react": "^19.1.9", "@types/react-dom": "^19.1.7", + "@vitejs/plugin-react": "^5.0.0", + "@vitest/ui": "^3.2.4", "eslint": "^9.32.0", - "eslint-config-next": "^15.4.5", - "jest": "^30.0.5", - "jest-environment-jsdom": "^30.0.5", + "eslint-config-next": "^15.4.6", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "jsdom": "^26.1.0", "node-fetch": "^3.3.2", "postcss": "^8.4.40", "tailwindcss": "^4.1.11", - "ts-jest": "^29.4.0", - "typescript": "^5.5.0", - "undici": "^7.12.0" + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0", + "undici": "^7.13.0", + "vitest": "^3.2.4" } } } diff --git a/package.json b/package.json index 02e2f76..eb46081 100644 --- a/package.json +++ b/package.json @@ -17,17 +17,24 @@ "clean": "npm run clean --workspace=shared && npm run clean --workspace=web && npm run clean --workspace=mcp-server", "test": "npm run test --workspace=shared && npm run test --workspace=web && npm run test --workspace=mcp-server", "test:watch": "npm run test:watch --workspace=shared", - "test:coverage": "npm run test:coverage --workspace=shared && npm run test:coverage --workspace=web && npm run test:coverage --workspace=mcp-server" + "test:coverage": "npm run test:coverage --workspace=shared && npm run test:coverage --workspace=web && npm run test:coverage --workspace=mcp-server", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "lint:all": "npm run lint --workspace=shared && npm run lint --workspace=web && npm run lint --workspace=mcp-server", + "ci:tests": "npm run test --workspace=@turn-based-mcp/shared && npm run test --workspace=@turn-based-mcp/web && npm run test --workspace=@turn-based-mcp/mcp-server", + "ci": "npm run build --workspace=@turn-based-mcp/shared && npm run type-check && npm run build --workspace=@turn-based-mcp/web && npm run build --workspace=@turn-based-mcp/mcp-server && npm run ci:tests && npm run lint --workspace=@turn-based-mcp/web" }, "devDependencies": { + "@eslint/js": "^9.32.0", "@testing-library/jest-dom": "^6.6.4", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", - "@types/node": "^24.1.0", - "jest": "^30.0.5", - "ts-jest": "^29.4.0", - "typescript": "^5.5.0" + "@types/node": "^24.2.0", + "@vitest/ui": "^3.2.4", + "eslint": "^9.32.0", + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0", + "vitest": "^3.2.4" }, "engines": { "node": ">=18.0.0" diff --git a/shared/eslint.config.js b/shared/eslint.config.js new file mode 100644 index 0000000..3dea93f --- /dev/null +++ b/shared/eslint.config.js @@ -0,0 +1,41 @@ +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.{js,mjs,cjs,ts}'], + languageOptions: { + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + }, + rules: { + // Shared library specific rules + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + '@typescript-eslint/explicit-function-return-type': 'warn', + '@typescript-eslint/no-explicit-any': 'warn', + }, + }, + { + files: ['**/*.test.ts', '**/*.test.tsx', 'vitest.*.ts'], + rules: { + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + }, + }, + { + ignores: [ + 'node_modules/**', + 'dist/**', + 'coverage/**', + '*.config.js', + '*.config.ts', + 'games.db', + ], + } +); diff --git a/shared/jest.config.cjs b/shared/jest.config.cjs deleted file mode 100644 index 6a03a52..0000000 --- a/shared/jest.config.cjs +++ /dev/null @@ -1,35 +0,0 @@ -/** @type {import('jest').Config} */ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - roots: ['/src'], - testMatch: [ - '**/__tests__/**/*.+(ts|tsx|js)', - '**/*.(test|spec).+(ts|tsx|js)' - ], - transform: { - '^.+\\.(ts|tsx)$': ['ts-jest', { - useESM: true - }] - }, - collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', - '!src/**/*.d.ts', - '!src/**/index.ts' - ], - coverageDirectory: 'coverage', - coverageReporters: ['text', 'lcov', 'html'], - coverageThreshold: { - global: { - branches: 90, - functions: 90, - lines: 90, - statements: 90 - } - }, - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], - extensionsToTreatAsEsm: ['.ts'], - moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1' - } -}; \ No newline at end of file diff --git a/shared/jest.setup.js b/shared/jest.setup.js deleted file mode 100644 index 5db1a22..0000000 --- a/shared/jest.setup.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Jest setup for shared package tests - * - * Sets up test database before all tests and cleans up after - */ - -const { setupTestDatabase, teardownTestDatabase } = require('./dist/testing/test-database') - -// Setup test database before all tests -beforeAll(async () => { - try { - await setupTestDatabase(true) // Use in-memory database for speed - } catch (error) { - console.error('Failed to setup test database:', error) - throw error - } -}) - -// Cleanup test database after all tests -afterAll(async () => { - try { - await teardownTestDatabase() - } catch (error) { - console.error('Failed to teardown test database:', error) - // Don't throw here to avoid masking other test failures - } -}) diff --git a/shared/package.json b/shared/package.json index 5f45ca2..472e699 100644 --- a/shared/package.json +++ b/shared/package.json @@ -5,21 +5,43 @@ "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "./testing": { + "import": "./dist/testing/index.js", + "types": "./dist/testing/index.d.ts" + }, + "./constants": { + "import": "./dist/constants/index.js", + "types": "./dist/constants/index.d.ts" + } + }, "scripts": { "build": "tsc && fix-esm-import-path dist", "dev": "tsc --watch", "clean": "rm -rf dist", "type-check": "tsc --noEmit", - "test": "jest", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage" + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "test:ui": "vitest --ui", + "lint": "eslint .", + "lint:fix": "eslint . --fix" }, "devDependencies": { + "@eslint/js": "^9.32.0", + "@jest/types": "30.0.5", "@types/jest": "^30.0.0", + "@vitest/coverage-v8": "^3.2.4", + "@vitest/ui": "^3.2.4", + "eslint": "^9.32.0", "fix-esm-import-path": "^1.10.3", - "jest": "^30.0.5", - "ts-jest": "^29.4.0", - "typescript": "^5.5.0" + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0", + "vitest": "^3.2.4" }, "dependencies": { "@types/sqlite3": "^5.1.0", diff --git a/shared/src/constants/game-constants.test.ts b/shared/src/constants/game-constants.test.ts new file mode 100644 index 0000000..48f4664 --- /dev/null +++ b/shared/src/constants/game-constants.test.ts @@ -0,0 +1,102 @@ +/** + * Tests for game constants + */ + +import { + GAME_TYPES, + DIFFICULTIES, + DEFAULT_PLAYER_NAME, + DEFAULT_AI_DIFFICULTY, + PLAYER_IDS, + DIFFICULTY_DISPLAY, + isSupportedGameType, + isValidDifficulty, + getDifficultyDisplay +} from './game-constants' + +describe('Game Constants', () => { + describe('GAME_TYPES', () => { + it('should contain expected game types', () => { + expect(GAME_TYPES).toEqual(['tic-tac-toe', 'rock-paper-scissors']) + expect(GAME_TYPES).toHaveLength(2) + }) + + it('should be immutable array', () => { + // Readonly arrays are frozen in TypeScript but may not throw at runtime + const originalLength = GAME_TYPES.length + expect(GAME_TYPES).toHaveLength(originalLength) + }) + }) + + describe('DIFFICULTIES', () => { + it('should contain expected difficulty levels', () => { + expect(DIFFICULTIES).toEqual(['easy', 'medium', 'hard']) + expect(DIFFICULTIES).toHaveLength(3) + }) + + it('should be immutable array', () => { + // Readonly arrays are frozen in TypeScript but may not throw at runtime + const originalLength = DIFFICULTIES.length + expect(DIFFICULTIES).toHaveLength(originalLength) + }) + }) + + describe('Default values', () => { + it('should have correct defaults', () => { + expect(DEFAULT_PLAYER_NAME).toBe('Player') + expect(DEFAULT_AI_DIFFICULTY).toBe('medium') + }) + }) + + describe('PLAYER_IDS', () => { + it('should have standard player IDs', () => { + expect(PLAYER_IDS.HUMAN).toBe('player1') + expect(PLAYER_IDS.AI).toBe('ai') + }) + }) + + describe('DIFFICULTY_DISPLAY', () => { + it('should have display info for all difficulties', () => { + expect(DIFFICULTY_DISPLAY.easy).toEqual({ emoji: '😌', label: 'Easy' }) + expect(DIFFICULTY_DISPLAY.medium).toEqual({ emoji: '🎯', label: 'Medium' }) + expect(DIFFICULTY_DISPLAY.hard).toEqual({ emoji: '🔥', label: 'Hard' }) + }) + }) + + describe('Type guards', () => { + describe('isSupportedGameType', () => { + it('should return true for valid game types', () => { + expect(isSupportedGameType('tic-tac-toe')).toBe(true) + expect(isSupportedGameType('rock-paper-scissors')).toBe(true) + }) + + it('should return false for invalid game types', () => { + expect(isSupportedGameType('invalid')).toBe(false) + expect(isSupportedGameType('')).toBe(false) + expect(isSupportedGameType('checkers')).toBe(false) + }) + }) + + describe('isValidDifficulty', () => { + it('should return true for valid difficulties', () => { + expect(isValidDifficulty('easy')).toBe(true) + expect(isValidDifficulty('medium')).toBe(true) + expect(isValidDifficulty('hard')).toBe(true) + }) + + it('should return false for invalid difficulties', () => { + expect(isValidDifficulty('invalid')).toBe(false) + expect(isValidDifficulty('')).toBe(false) + expect(isValidDifficulty('extreme')).toBe(false) + }) + }) + }) + + describe('getDifficultyDisplay', () => { + it('should return correct display info for each difficulty', () => { + expect(getDifficultyDisplay('easy')).toEqual({ emoji: '😌', label: 'Easy' }) + expect(getDifficultyDisplay('medium')).toEqual({ emoji: '🎯', label: 'Medium' }) + expect(getDifficultyDisplay('hard')).toEqual({ emoji: '🔥', label: 'Hard' }) + }) + }) +}) \ No newline at end of file diff --git a/shared/src/constants/game-constants.ts b/shared/src/constants/game-constants.ts new file mode 100644 index 0000000..484c315 --- /dev/null +++ b/shared/src/constants/game-constants.ts @@ -0,0 +1,86 @@ +/** + * Shared game constants + * Single source of truth for game types, difficulties, player IDs, and default values + */ + +/** + * Supported game types + */ +export const GAME_TYPES = ['tic-tac-toe', 'rock-paper-scissors'] as const + +/** + * Available AI difficulty levels + */ +export const DIFFICULTIES = ['easy', 'medium', 'hard'] as const + +/** + * Standard player IDs used across the system + */ +export const PLAYER_IDS = { + HUMAN: 'player1', + PLAYER2: 'player2', + AI: 'ai' +} as const + +/** + * Game status values + */ +export const GAME_STATUSES = ['waiting', 'playing', 'finished'] as const + +/** + * Derive types from constants + */ +export type GameType = typeof GAME_TYPES[number] +export type Difficulty = typeof DIFFICULTIES[number] +export type PlayerId = typeof PLAYER_IDS[keyof typeof PLAYER_IDS] +export type GameStatus = typeof GAME_STATUSES[number] + +/** + * Default player configurations + */ +export const DEFAULT_PLAYER_NAME = 'Player' +export const DEFAULT_AI_DIFFICULTY: Difficulty = 'medium' + +/** + * Difficulty display configuration + */ +export const DIFFICULTY_DISPLAY = { + easy: { emoji: '😌', label: 'Easy' }, + medium: { emoji: '🎯', label: 'Medium' }, + hard: { emoji: '🔥', label: 'Hard' } +} as const + +/** + * Type guard to check if a string is a supported game type + */ +export function isSupportedGameType(gameType: string): gameType is GameType { + return GAME_TYPES.includes(gameType as GameType) +} + +/** + * Type guard to check if a string is a valid difficulty level + */ +export function isValidDifficulty(difficulty: string): difficulty is Difficulty { + return DIFFICULTIES.includes(difficulty as Difficulty) +} + +/** + * Type guard to check if a string is a valid player ID + */ +export function isValidPlayerId(playerId: string): playerId is PlayerId { + return Object.values(PLAYER_IDS).includes(playerId as PlayerId) +} + +/** + * Type guard to check if a string is a valid game status + */ +export function isValidGameStatus(status: string): status is GameStatus { + return GAME_STATUSES.includes(status as GameStatus) +} + +/** + * Get difficulty display configuration + */ +export function getDifficultyDisplay(difficulty: Difficulty): { emoji: string; label: string } { + return DIFFICULTY_DISPLAY[difficulty] +} \ No newline at end of file diff --git a/shared/src/constants/index.ts b/shared/src/constants/index.ts new file mode 100644 index 0000000..9c7cfbf --- /dev/null +++ b/shared/src/constants/index.ts @@ -0,0 +1,5 @@ +/** + * Constants index - exports all game constants + */ + +export * from './game-constants' \ No newline at end of file diff --git a/shared/src/games/rock-paper-scissors.ts b/shared/src/games/rock-paper-scissors.ts index c40ca53..384981f 100644 --- a/shared/src/games/rock-paper-scissors.ts +++ b/shared/src/games/rock-paper-scissors.ts @@ -95,7 +95,7 @@ export class RockPaperScissorsGame implements Game { newRounds[gameState.currentRound] = currentRound; let newCurrentRound = gameState.currentRound; - let newScores = { ...gameState.scores }; + const newScores = { ...gameState.scores }; let newCurrentPlayerId = gameState.currentPlayerId; // If both players have made their choices, resolve the round @@ -211,18 +211,19 @@ export class RockPaperScissorsGame implements Game { * Creates the initial game state for a new rock-paper-scissors game * * @param players - Array of exactly 2 players - * @returns Initial RPSGameState set up for a best-of-3 match + * @param options - Optional configuration including maxRounds + * @returns Initial RPSGameState set up for a configurable number of rounds * * @description * Sets up a new game with: - * - 3 empty rounds (best-of-3 format) + * - Configurable number of rounds (default: 3 for best-of-3 format) * - All scores initialized to 0 * - First player goes first * - Game status set to 'playing' * - Current round set to 0 */ - getInitialState(players: Player[]): RPSGameState { - const maxRounds = 3; // Best of 3 + getInitialState(players: Player[], options?: { maxRounds?: number }): RPSGameState { + const maxRounds = options?.maxRounds || 3; // Default to best of 3 const rounds = Array.from({ length: maxRounds }, () => ({})); const scores: Record = {}; diff --git a/shared/src/games/tic-tac-toe.ts b/shared/src/games/tic-tac-toe.ts index b23e2ab..9cbb0c7 100644 --- a/shared/src/games/tic-tac-toe.ts +++ b/shared/src/games/tic-tac-toe.ts @@ -189,32 +189,53 @@ export class TicTacToeGame implements Game { /** * Creates the initial game state for a new tic-tac-toe game * - * @param players - Array of exactly 2 players (first player gets X, second gets O) - * @returns Initial TicTacToeGameState with empty board and first player's turn + * @param players - Array of exactly 2 players + * @param options - Optional game configuration + * @returns Initial TicTacToeGameState with empty board * * @description * Sets up a new game with: * - Empty 3x3 board (all cells null) - * - Player 1 assigned 'X' symbol and goes first - * - Player 2 assigned 'O' symbol + * - Player symbol assignment (X always goes first, O goes second) + * - Configurable turn order (who gets X and goes first) * - Game status set to 'playing' * - Timestamps initialized to current time */ - getInitialState(players: Player[]): TicTacToeGameState { + getInitialState(players: Player[], options?: { firstPlayerId?: string }): TicTacToeGameState { const board: Board = [ [null, null, null], [null, null, null], [null, null, null] ]; + // Determine who gets X (goes first) and who gets O (goes second) + let firstPlayer: Player; + let secondPlayer: Player; + + if (options?.firstPlayerId) { + const firstPlayerIndex = players.findIndex(p => p.id === options.firstPlayerId); + if (firstPlayerIndex !== -1) { + firstPlayer = players[firstPlayerIndex]; + secondPlayer = players[1 - firstPlayerIndex]; + } else { + // Fallback to default order if specified player not found + firstPlayer = players[0]; + secondPlayer = players[1]; + } + } else { + // Default: first player in array goes first + firstPlayer = players[0]; + secondPlayer = players[1]; + } + const playerSymbols: Record = {}; - playerSymbols[players[0].id] = 'X'; - playerSymbols[players[1].id] = 'O'; + playerSymbols[firstPlayer.id] = 'X'; + playerSymbols[secondPlayer.id] = 'O'; return { id: crypto.randomUUID(), players, - currentPlayerId: players[0].id, + currentPlayerId: firstPlayer.id, status: 'playing', createdAt: new Date(), updatedAt: new Date(), diff --git a/shared/src/index.ts b/shared/src/index.ts index 49e92fc..e8873f9 100644 --- a/shared/src/index.ts +++ b/shared/src/index.ts @@ -3,4 +3,4 @@ export type * from './types/games'; export * from './games/index'; export * from './utils/index'; export * from './storage/index'; -export * from './testing/index'; +export * from './constants/index'; diff --git a/shared/src/storage/game-storage.test.ts b/shared/src/storage/game-storage.test.ts index f911449..a438172 100644 --- a/shared/src/storage/game-storage.test.ts +++ b/shared/src/storage/game-storage.test.ts @@ -1,11 +1,12 @@ +import { vi } from 'vitest' import * as gameStorage from './game-storage'; import * as sqliteStorage from './sqlite-storage'; -import type { GameSession, Player } from '../types/game'; +import type { GameSession } from '../types/game'; import type { TicTacToeGameState, RPSGameState } from '../types/games'; // Mock sqlite-storage module -jest.mock('./sqlite-storage'); -const mockSqliteStorage = sqliteStorage as jest.Mocked; +vi.mock('./sqlite-storage'); +const mockSqliteStorage = sqliteStorage as any; describe('Game Storage', () => { const mockTicTacToeGameSession: GameSession = { @@ -57,7 +58,7 @@ describe('Game Storage', () => { }; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('TicTacToe Game Functions', () => { diff --git a/shared/src/storage/mcp-api-client.test.ts b/shared/src/storage/mcp-api-client.test.ts index 413b213..e0b675a 100644 --- a/shared/src/storage/mcp-api-client.test.ts +++ b/shared/src/storage/mcp-api-client.test.ts @@ -1,9 +1,10 @@ +import { vi } from 'vitest' import * as mcpApiClient from './mcp-api-client'; import type { GameSession, Player } from '../types/game'; import type { TicTacToeGameState, RPSGameState } from '../types/games'; // Mock fetch globally -const mockFetch = jest.fn() as jest.MockedFunction; +const mockFetch = vi.fn() as any; global.fetch = mockFetch; describe('MCP API Client', () => { @@ -12,11 +13,11 @@ describe('MCP API Client', () => { ok, status, statusText: ok ? 'OK' : 'Error', - json: jest.fn().mockResolvedValue(data) + json: vi.fn().mockResolvedValue(data) } as any); beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); // Reset environment variables process.env = { ...originalEnv }; }); @@ -63,7 +64,7 @@ describe('MCP API Client', () => { it('should use custom base URL from environment', async () => { // Need to delete the module from cache since environment variable is read at module load time - jest.resetModules(); + vi.resetModules(); process.env.WEB_API_BASE = 'http://custom-host:8080'; @@ -89,7 +90,7 @@ describe('MCP API Client', () => { it('should handle fetch errors gracefully', async () => { mockFetch.mockRejectedValue(new Error('Network error')); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const result = await mcpApiClient.getTicTacToeGameForMCP('test-game-1'); @@ -101,7 +102,7 @@ describe('MCP API Client', () => { it('should handle HTTP errors gracefully', async () => { mockFetch.mockResolvedValue(mockResponse({}, false, 404)); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const result = await mcpApiClient.getTicTacToeGameForMCP('test-game-1'); @@ -168,7 +169,7 @@ describe('MCP API Client', () => { it('should handle HTTP errors and throw', async () => { mockFetch.mockResolvedValue(mockResponse({}, false, 500)); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); await expect(mcpApiClient.createTicTacToeGameForMCP(mockPlayers)).rejects.toThrow(); expect(consoleSpy).toHaveBeenCalledWith('Error creating tic-tac-toe game via API:', expect.any(Error)); @@ -178,7 +179,7 @@ describe('MCP API Client', () => { it('should handle network errors and throw', async () => { mockFetch.mockRejectedValue(new Error('Network error')); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); await expect(mcpApiClient.createTicTacToeGameForMCP(mockPlayers)).rejects.toThrow(); expect(consoleSpy).toHaveBeenCalledWith('Error creating tic-tac-toe game via API:', expect.any(Error)); @@ -229,7 +230,7 @@ describe('MCP API Client', () => { it('should handle HTTP errors and throw', async () => { mockFetch.mockResolvedValue(mockResponse({}, false, 400)); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); await expect(mcpApiClient.makeTicTacToeMove('test-game-1', mockMove, 'player1')).rejects.toThrow(); expect(consoleSpy).toHaveBeenCalledWith('Error making tic-tac-toe move via API:', expect.any(Error)); @@ -279,7 +280,7 @@ describe('MCP API Client', () => { it('should handle errors gracefully', async () => { mockFetch.mockRejectedValue(new Error('Network error')); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const result = await mcpApiClient.getRPSGameForMCP('test-rps-1'); @@ -327,7 +328,7 @@ describe('MCP API Client', () => { it('should handle move errors and throw', async () => { mockFetch.mockResolvedValue(mockResponse({}, false, 400)); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); await expect(mcpApiClient.makeRPSMove('test-rps-1', mockMove, 'player1')).rejects.toThrow(); expect(consoleSpy).toHaveBeenCalledWith('Error making RPS move via API:', expect.any(Error)); diff --git a/shared/src/storage/mcp-api-client.ts b/shared/src/storage/mcp-api-client.ts index 3fcd953..0a5d7cc 100644 --- a/shared/src/storage/mcp-api-client.ts +++ b/shared/src/storage/mcp-api-client.ts @@ -1,43 +1,20 @@ -import type { GameSession } from '../types/game' -import type { TicTacToeGameState, RPSGameState } from '../types/games' - -// Web app base URL for API calls (when called from MCP server) -const WEB_API_BASE = process.env.WEB_API_BASE || 'http://localhost:3000' - -// HTTP client functions for MCP server -async function httpGet(url: string): Promise { - const response = await fetch(url) - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`) - } - return response.json() -} - -async function httpPost(url: string, data: any): Promise { - const response = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(data) - }) - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`) - } - return response.json() -} +import type { GameSession, Player } from '../types/game' +import type { TicTacToeGameState, RPSGameState, TicTacToeMove, RPSMove } from '../types/games' +import { httpGet, httpPost, WEB_API_BASE } from '../utils/http-client' // MCP-specific API functions that make HTTP calls to web app export async function getTicTacToeGameForMCP(gameId: string): Promise | undefined> { try { // Get all games and find the one we want const games = await httpGet(`${WEB_API_BASE}/api/games/tic-tac-toe`) - return games.find((game: any) => game.gameState?.id === gameId) + return games.find((game: GameSession) => game.gameState?.id === gameId) } catch (error) { console.error('Error fetching tic-tac-toe game via API:', error) return undefined } } -export async function createTicTacToeGameForMCP(players: any[]): Promise> { +export async function createTicTacToeGameForMCP(players: Player[]): Promise> { try { const playerName = players.find(p => !p.isAI)?.name || 'Player' return await httpPost(`${WEB_API_BASE}/api/games/tic-tac-toe`, { playerName }) @@ -47,7 +24,7 @@ export async function createTicTacToeGameForMCP(players: any[]): Promise> { +export async function makeTicTacToeMove(gameId: string, move: TicTacToeMove, playerId: string): Promise> { try { return await httpPost(`${WEB_API_BASE}/api/games/tic-tac-toe/${gameId}/move`, { move, playerId }) } catch (error) { @@ -59,14 +36,14 @@ export async function makeTicTacToeMove(gameId: string, move: any, playerId: str export async function getRPSGameForMCP(gameId: string): Promise | undefined> { try { const games = await httpGet(`${WEB_API_BASE}/api/games/rock-paper-scissors/mcp`) - return games.find((game: any) => game.gameState?.id === gameId) + return games.find((game: GameSession) => game.gameState?.id === gameId) } catch (error) { console.error('Error fetching RPS game via API:', error) return undefined } } -export async function makeRPSMove(gameId: string, move: any, playerId: string): Promise> { +export async function makeRPSMove(gameId: string, move: RPSMove, playerId: string): Promise> { try { return await httpPost(`${WEB_API_BASE}/api/games/rock-paper-scissors/${gameId}/move`, { move, playerId }) } catch (error) { diff --git a/shared/src/storage/sqlite-storage.test.ts b/shared/src/storage/sqlite-storage.test.ts index 9bb39a6..2c5cdd4 100644 --- a/shared/src/storage/sqlite-storage.test.ts +++ b/shared/src/storage/sqlite-storage.test.ts @@ -1,27 +1,30 @@ +import { vi } from 'vitest' import * as sqliteStorage from './sqlite-storage'; // Mock sqlite3 to prevent actual database operations -jest.mock('sqlite3', () => ({ - Database: jest.fn().mockImplementation(() => ({ - run: jest.fn(function(sql, params, callback) { - if (callback) { - // Mock the 'this' context with lastID and changes - callback.call({ lastID: 1, changes: 1 }); - } - }), - get: jest.fn((sql, params, callback) => { - if (callback) callback(null, null); - }), - all: jest.fn((sql, params, callback) => { - if (callback) callback(null, []); - }), - close: jest.fn((callback) => { - if (callback) callback(); - }), - serialize: jest.fn((fn) => { - if (fn) fn(); - }) - })) +vi.mock('sqlite3', () => ({ + default: { + Database: vi.fn().mockImplementation(() => ({ + run: vi.fn(function(sql, params, callback) { + if (callback) { + // Mock the 'this' context with lastID and changes + callback.call({ lastID: 1, changes: 1 }); + } + }), + get: vi.fn((sql, params, callback) => { + if (callback) callback(null, null); + }), + all: vi.fn((sql, params, callback) => { + if (callback) callback(null, []); + }), + close: vi.fn((callback) => { + if (callback) callback(); + }), + serialize: vi.fn((fn) => { + if (fn) fn(); + }) + })) + } })); // Integration-style tests for SQLite storage @@ -29,12 +32,12 @@ jest.mock('sqlite3', () => ({ describe('SQLite Storage', () => { // Mock console.log to reduce noise during tests beforeAll(() => { - jest.spyOn(console, 'log').mockImplementation(() => {}); - jest.spyOn(console, 'error').mockImplementation(() => {}); + vi.spyOn(console, 'log').mockImplementation(() => {}); + vi.spyOn(console, 'error').mockImplementation(() => {}); }); afterAll(async () => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); describe('Module exports', () => { diff --git a/shared/src/storage/sqlite-storage.ts b/shared/src/storage/sqlite-storage.ts index 0f20f1f..d2a0a1f 100644 --- a/shared/src/storage/sqlite-storage.ts +++ b/shared/src/storage/sqlite-storage.ts @@ -1,5 +1,4 @@ import sqlite3 from 'sqlite3' -import { promisify } from 'util' import path from 'path' import type { GameSession } from '../types/game' import type { TicTacToeGameState, RPSGameState } from '../types/games' @@ -11,7 +10,7 @@ let initializationPromise: Promise | null = null // Database file path - store in project root so both web app and MCP server can access // Use environment variable if set, otherwise use a path in the current working directory // During tests, use an in-memory database for speed and isolation -const DB_PATH = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined +const DB_PATH = process.env.NODE_ENV === 'test' || process.env.VITEST !== undefined ? ':memory:' : process.env.GAMES_DB_PATH || path.join(process.cwd(), 'games.db') diff --git a/shared/src/testing/api-test-utils.ts b/shared/src/testing/api-test-utils.ts new file mode 100644 index 0000000..d1d38b5 --- /dev/null +++ b/shared/src/testing/api-test-utils.ts @@ -0,0 +1,144 @@ +/** + * Shared API Test Utilities + * + * Provides reusable mock factories and test utilities for API route testing + * to eliminate duplication across test files. + */ + +import { vi } from 'vitest' +import type { GameSession, Player, BaseGameState, GameType } from '../types/game' +import type { TicTacToeGameState, RPSGameState, TicTacToeMove, RPSMove } from '../types/games' + +/** + * Generic game mock factory + * Creates a mock game instance with all required methods + */ +export function createGameMock(): { + getInitialState: ReturnType; + validateMove: ReturnType; + applyMove: ReturnType; + checkGameEnd: ReturnType; + getValidMoves: ReturnType; +} { + return { + getInitialState: vi.fn(), + validateMove: vi.fn(), + applyMove: vi.fn(), + checkGameEnd: vi.fn(), + getValidMoves: vi.fn() + } +} + +/** + * Create mock players for testing + */ +export function createMockPlayers(): Player[] { + return [ + { id: 'player1', name: 'Player', isAI: false }, + { id: 'ai', name: 'AI', isAI: true } + ] +} + +/** + * Create a mock TicTacToe game state + */ +export function createMockTicTacToeGameState(overrides: Partial = {}): TicTacToeGameState { + return { + id: 'test-game-1', + players: createMockPlayers(), + currentPlayerId: 'player1', + status: 'playing', + createdAt: new Date('2024-01-01T10:00:00Z'), + updatedAt: new Date('2024-01-01T10:00:00Z'), + board: [ + [null, null, null], + [null, null, null], + [null, null, null] + ], + playerSymbols: { + player1: 'X', + ai: 'O' + }, + ...overrides + } +} + +/** + * Create a mock Rock Paper Scissors game state + */ +export function createMockRPSGameState(overrides: Partial = {}): RPSGameState { + return { + id: 'test-rps-1', + players: createMockPlayers(), + currentPlayerId: 'player1', + status: 'playing', + createdAt: new Date('2024-01-01T10:00:00Z'), + updatedAt: new Date('2024-01-01T10:00:00Z'), + rounds: [], + currentRound: 0, + maxRounds: 3, + scores: { + player1: 0, + ai: 0 + }, + ...overrides + } +} + +/** + * Create a mock game session + */ +export function createMockGameSession(gameState: T, gameType: GameType): GameSession { + return { + gameState, + gameType, + history: [], + difficulty: 'medium' + } +} + +/** + * Vitest mock configuration for shared package games + * Use this to create consistent mocks across API route tests + */ +export function createSharedGameMocks(gameClass: string): { + mockImplementation: Record; + mockGame: ReturnType; +} { + const mockGame = createGameMock() + + return { + mockImplementation: { + ...vi.importActual('@turn-based-mcp/shared'), + [gameClass]: vi.fn().mockImplementation(() => mockGame) + }, + mockGame + } +} + +/** + * Create storage function mocks for a specific game type + */ +export function createStorageMocks(gameType: GameType): Record> { + if (gameType === 'tic-tac-toe') { + return { + getTicTacToeGame: vi.fn(), + setTicTacToeGame: vi.fn(), + getAllTicTacToeGames: vi.fn() + } + } else { + return { + getRPSGame: vi.fn(), + setRPSGame: vi.fn(), + getAllRPSGames: vi.fn() + } + } +} + +/** + * Standard test moves for different games + */ +export const TEST_MOVES = { + ticTacToe: { row: 0, col: 0 } as TicTacToeMove, + rockPaperScissors: { choice: 'rock' } as RPSMove +} \ No newline at end of file diff --git a/shared/src/testing/index.ts b/shared/src/testing/index.ts index f7b1120..9e8ed32 100644 --- a/shared/src/testing/index.ts +++ b/shared/src/testing/index.ts @@ -12,3 +12,18 @@ export { getTestDatabase, isTestDatabaseReady } from './test-database' + +export { + createGameMock, + createMockPlayers, + createMockTicTacToeGameState, + createMockRPSGameState, + createMockGameSession, + createSharedGameMocks, + createStorageMocks, + TEST_MOVES +} from './api-test-utils' + +export { + setupStandardTestDatabase +} from './vitest-setup' diff --git a/shared/src/testing/test-database.ts b/shared/src/testing/test-database.ts index 764ebe8..607861a 100644 --- a/shared/src/testing/test-database.ts +++ b/shared/src/testing/test-database.ts @@ -51,7 +51,7 @@ export async function setupTestDatabase( } // Create tables synchronously to ensure they exist before tests run - const createTables = async () => { + const createTables = async (): Promise => { try { await createTicTacToeTable() await createRPSTable() diff --git a/shared/src/testing/vitest-setup.ts b/shared/src/testing/vitest-setup.ts new file mode 100644 index 0000000..56473d2 --- /dev/null +++ b/shared/src/testing/vitest-setup.ts @@ -0,0 +1,35 @@ +/** + * Shared Vitest Setup Functions + * + * Provides standardized test setup and teardown functions to reduce + * duplication across package vitest setup files. + */ + +import { beforeAll, afterAll } from 'vitest' +import { setupTestDatabase, teardownTestDatabase } from './test-database' + +/** + * Standard database setup for all packages + * Call this in vitest.setup.ts files to ensure consistent database setup + */ +export function setupStandardTestDatabase(): void { + // Setup test database before all tests + beforeAll(async () => { + try { + await setupTestDatabase(true) // Use in-memory database for speed + } catch (error) { + console.error('Failed to setup test database:', error) + throw error + } + }) + + // Cleanup test database after all tests + afterAll(async () => { + try { + await teardownTestDatabase() + } catch (error) { + console.error('Failed to teardown test database:', error) + // Don't throw here to avoid masking other test failures + } + }) +} \ No newline at end of file diff --git a/shared/src/types/game.ts b/shared/src/types/game.ts index 869cbc7..8b25525 100644 --- a/shared/src/types/game.ts +++ b/shared/src/types/game.ts @@ -1,5 +1,7 @@ // Core game types -export type PlayerId = 'player1' | 'player2' | 'ai'; +import type { PlayerId, GameStatus, GameType, Difficulty } from '../constants/game-constants' + +export type { PlayerId, GameStatus, GameType, Difficulty } export interface Player { id: PlayerId; @@ -7,8 +9,6 @@ export interface Player { isAI: boolean; } -export type GameStatus = 'waiting' | 'playing' | 'finished'; - export interface BaseGameState { id: string; players: Player[]; @@ -19,7 +19,7 @@ export interface BaseGameState { updatedAt: Date; } -export interface GameMove { +export interface GameMove { playerId: PlayerId; move: T; timestamp: Date; @@ -36,17 +36,13 @@ export interface Game { applyMove(gameState: TGameState, move: TMove, playerId: PlayerId): TGameState; checkGameEnd(gameState: TGameState): GameResult | null; getValidMoves(gameState: TGameState, playerId: PlayerId): TMove[]; - getInitialState(players: Player[]): TGameState; + getInitialState(players: Player[], options?: Record): TGameState; } -export type GameType = 'tic-tac-toe' | 'rock-paper-scissors'; - -export type Difficulty = 'easy' | 'medium' | 'hard'; - // Generic game session for API communication export interface GameSession { gameState: TGameState; gameType: GameType; history: GameMove[]; - aiDifficulty?: Difficulty; + difficulty?: Difficulty; } diff --git a/shared/src/types/games.ts b/shared/src/types/games.ts index 2cb874b..06affdd 100644 --- a/shared/src/types/games.ts +++ b/shared/src/types/games.ts @@ -1,4 +1,4 @@ -import { BaseGameState, GameMove } from './game'; +import { BaseGameState } from './game'; // Tic-tac-toe specific types export type CellValue = 'X' | 'O' | null; diff --git a/shared/src/utils/http-client.ts b/shared/src/utils/http-client.ts new file mode 100644 index 0000000..70d4928 --- /dev/null +++ b/shared/src/utils/http-client.ts @@ -0,0 +1,44 @@ +/** + * Shared HTTP client utilities for communicating with the web API + * + * Provides centralized HTTP functions to eliminate duplication between + * the MCP server and shared library API clients. + */ + +// Web app base URL for API calls (configurable via environment) +export const WEB_API_BASE = process.env.WEB_API_BASE || 'http://localhost:3000' + +/** + * Performs an HTTP GET request + * + * @param url - The URL to fetch from + * @returns Promise resolving to the JSON response + * @throws Error if the request fails or returns non-2xx status + */ +export async function httpGet(url: string): Promise { + const response = await fetch(url) + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`) + } + return response.json() +} + +/** + * Performs an HTTP POST request with JSON data + * + * @param url - The URL to post to + * @param data - The data to send in the request body + * @returns Promise resolving to the JSON response + * @throws Error if the request fails or returns non-2xx status + */ +export async function httpPost(url: string, data: unknown): Promise { + const response = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }) + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`) + } + return response.json() +} \ No newline at end of file diff --git a/shared/src/utils/index.ts b/shared/src/utils/index.ts index a36c4cd..ff76d1c 100644 --- a/shared/src/utils/index.ts +++ b/shared/src/utils/index.ts @@ -34,4 +34,7 @@ export function getGameDisplayName(gameType: GameType): string { } } +// Re-export HTTP client utilities +export * from './http-client'; + diff --git a/shared/tsconfig.json b/shared/tsconfig.json index 676f675..517001e 100644 --- a/shared/tsconfig.json +++ b/shared/tsconfig.json @@ -15,5 +15,5 @@ "skipLibCheck": true }, "include": ["src/**/*"], - "exclude": ["dist", "node_modules"] + "exclude": ["dist", "node_modules", "**/*.test.ts", "**/*.test.tsx"] } diff --git a/shared/vitest.config.ts b/shared/vitest.config.ts new file mode 100644 index 0000000..0875eb4 --- /dev/null +++ b/shared/vitest.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + environment: 'node', + globals: true, // Jest-style describe, it, expect + setupFiles: ['./vitest.setup.ts'], + coverage: { + provider: 'v8', + reporter: ['text', 'lcov', 'html'], + thresholds: { + branches: 90, + functions: 90, + lines: 90, + statements: 90 + }, + include: ['src/**/*.{ts,tsx}'], + exclude: ['src/**/*.d.ts', 'src/**/index.ts'] + } + }, +}) diff --git a/shared/vitest.setup.ts b/shared/vitest.setup.ts new file mode 100644 index 0000000..8fa05da --- /dev/null +++ b/shared/vitest.setup.ts @@ -0,0 +1,10 @@ +/** + * Vitest setup for shared package tests + * + * Uses local test setup utilities since this is the package that defines them + */ + +import { setupStandardTestDatabase } from './src/testing/vitest-setup' + +// Setup standard test database using local utility +setupStandardTestDatabase() diff --git a/web/.eslintrc.json b/web/.eslintrc.json deleted file mode 100644 index 6b10a5b..0000000 --- a/web/.eslintrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": [ - "next/core-web-vitals", - "next/typescript" - ] -} diff --git a/web/eslint.config.js b/web/eslint.config.js new file mode 100644 index 0000000..efbb807 --- /dev/null +++ b/web/eslint.config.js @@ -0,0 +1,62 @@ +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import reactPlugin from 'eslint-plugin-react'; +import reactHooksPlugin from 'eslint-plugin-react-hooks'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + { + files: ['**/*.{js,mjs,cjs,ts,tsx}'], + plugins: { + react: reactPlugin, + 'react-hooks': reactHooksPlugin, + }, + languageOptions: { + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + }, + settings: { + react: { + version: 'detect', + }, + }, + rules: { + // React specific rules + ...reactPlugin.configs.recommended.rules, + ...reactHooksPlugin.configs.recommended.rules, + + // TypeScript rules + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }], + '@typescript-eslint/no-explicit-any': 'warn', + + // Next.js specific rules + 'react/react-in-jsx-scope': 'off', // Not needed in Next.js 13+ + 'react/prop-types': 'off', // Using TypeScript for prop validation + }, + }, + { + files: ['**/*.test.{ts,tsx}', 'vitest.*.ts'], + rules: { + '@typescript-eslint/no-explicit-any': 'off', + 'react/display-name': 'off', + }, + }, + { + ignores: [ + 'node_modules/**', + '.next/**', + 'coverage/**', + '*.config.js', + '*.config.ts', + 'games.db', + ], + } +); diff --git a/web/jest.config.js b/web/jest.config.js deleted file mode 100644 index 138399c..0000000 --- a/web/jest.config.js +++ /dev/null @@ -1,45 +0,0 @@ -/** @type {import('jest').Config} */ -const nextJest = require('next/jest'); - -const createJestConfig = nextJest({ - // Provide the path to your Next.js app to load next.config.js and .env files - dir: './', -}); - -// Add any custom config to be passed to Jest -const customJestConfig = { - setupFiles: ['/jest.setup.next-api.js'], - setupFilesAfterEnv: ['/jest.setup.js'], - testEnvironment: 'jsdom', - testMatch: [ - '**/__tests__/**/*.+(ts|tsx|js)', - '**/*.(test|spec).+(ts|tsx|js)' - ], - collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', - '!src/**/*.d.ts', - '!src/**/index.ts', - '!src/app/globals.css' - ], - coverageDirectory: 'coverage', - coverageReporters: ['text', 'lcov', 'html'], - coverageThreshold: { - global: { - branches: 80, - functions: 80, - lines: 80, - statements: 80 - } - }, - moduleNameMapper: { - '^@/(.*)$': '/src/$1', - '^@turn-based-mcp/shared$': '/../shared/src' - }, - testPathIgnorePatterns: [ - '/node_modules/', - '/.next/' - ] -}; - -// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async -module.exports = createJestConfig(customJestConfig); \ No newline at end of file diff --git a/web/jest.polyfills.js b/web/jest.polyfills.js deleted file mode 100644 index d6ab754..0000000 --- a/web/jest.polyfills.js +++ /dev/null @@ -1,85 +0,0 @@ -// Global polyfills for Jest tests -// Must run before any Next.js imports to establish Web API environment - -// Force polyfills to be set synchronously before any module loading -(function setupPolyfills() { - 'use strict'; - - // Set up all Web APIs immediately - - // Crypto - if (!globalThis.crypto) { - try { - const { webcrypto } = require('node:crypto'); - globalThis.crypto = webcrypto; - } catch (e) { - // Fallback crypto implementation - globalThis.crypto = { - randomUUID: () => { - // Simple UUID v4 implementation for testing - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - const r = Math.random() * 16 | 0; - const v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); - } - }; - } - } - - // Ensure randomUUID method exists even if crypto is partially supported - if (globalThis.crypto && !globalThis.crypto.randomUUID) { - globalThis.crypto.randomUUID = () => { - // Simple UUID v4 implementation for testing - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - const r = Math.random() * 16 | 0; - const v = c === 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); - }; - } - - // Text encoding - if (!globalThis.TextEncoder) { - try { - const { TextEncoder, TextDecoder } = require('util'); - globalThis.TextEncoder = TextEncoder; - globalThis.TextDecoder = TextDecoder; - } catch (e) {} - } - - // Streams - if (!globalThis.ReadableStream) { - try { - const streams = require('node:stream/web'); - globalThis.ReadableStream = streams.ReadableStream; - globalThis.WritableStream = streams.WritableStream; - globalThis.TransformStream = streams.TransformStream; - } catch (e) {} - } - - // Fetch APIs - be very careful here - if (!globalThis.fetch) { - try { - // Use undici but catch all errors and continue - const undici = require('undici'); - if (undici && undici.fetch) { - globalThis.fetch = undici.fetch; - } - if (undici && undici.Request && !globalThis.Request) { - globalThis.Request = undici.Request; - } - if (undici && undici.Response && !globalThis.Response) { - globalThis.Response = undici.Response; - } - if (undici && undici.Headers && !globalThis.Headers) { - globalThis.Headers = undici.Headers; - } - if (undici && undici.FormData && !globalThis.FormData) { - globalThis.FormData = undici.FormData; - } - } catch (e) { - // Silent fail for fetch APIs - Next.js will handle this - } - } -})(); diff --git a/web/jest.setup.api.js b/web/jest.setup.api.js deleted file mode 100644 index 7c85061..0000000 --- a/web/jest.setup.api.js +++ /dev/null @@ -1,60 +0,0 @@ -// Setup file for API route tests running in Node.js environment -// Provides Web API polyfills needed for Next.js server-side code - -// Polyfill crypto -const { webcrypto } = require('node:crypto'); -if (!globalThis.crypto) { - globalThis.crypto = webcrypto; -} - -// Polyfill streams -try { - const { ReadableStream, WritableStream, TransformStream } = require('node:stream/web'); - if (!globalThis.ReadableStream) { - globalThis.ReadableStream = ReadableStream; - } - if (!globalThis.WritableStream) { - globalThis.WritableStream = WritableStream; - } - if (!globalThis.TransformStream) { - globalThis.TransformStream = TransformStream; - } -} catch (error) { - // Streams might not be available in older Node versions -} - -// Add fetch polyfill using undici -const { fetch, Request, Response, Headers, FormData } = require('undici'); -globalThis.fetch = fetch; -globalThis.Request = Request; -globalThis.Response = Response; -globalThis.Headers = Headers; -globalThis.FormData = FormData; - -// Set up text encoding/decoding -const { TextEncoder, TextDecoder } = require('util'); -globalThis.TextEncoder = TextEncoder; -globalThis.TextDecoder = TextDecoder; - -// Set up test database for API route tests -const { setupTestDatabase, teardownTestDatabase } = require('@turn-based-mcp/shared'); - -// Setup test database before all tests -beforeAll(async () => { - try { - await setupTestDatabase(true); // Use in-memory database for speed - } catch (error) { - console.error('Failed to setup test database:', error); - throw error; - } -}); - -// Cleanup test database after all tests -afterAll(async () => { - try { - await teardownTestDatabase(); - } catch (error) { - console.error('Failed to teardown test database:', error); - // Don't throw here to avoid masking other test failures - } -}); diff --git a/web/jest.setup.js b/web/jest.setup.js deleted file mode 100644 index 60a0d6c..0000000 --- a/web/jest.setup.js +++ /dev/null @@ -1,29 +0,0 @@ -// Optional: configure or set up a testing framework before each test. -// If you delete this file, remove `setupFilesAfterEnv` from `jest.config.js` - -// Used for __tests__/testing-library.js -// Learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; - -// Set up test database for all tests -import { setupTestDatabase, teardownTestDatabase } from '@turn-based-mcp/shared'; - -// Setup test database before all tests -beforeAll(async () => { - try { - await setupTestDatabase(true); // Use in-memory database for speed - } catch (error) { - console.error('Failed to setup test database:', error); - throw error; - } -}); - -// Cleanup test database after all tests -afterAll(async () => { - try { - await teardownTestDatabase(); - } catch (error) { - console.error('Failed to teardown test database:', error); - // Don't throw here to avoid masking other test failures - } -}); \ No newline at end of file diff --git a/web/jest.setup.next-api.js b/web/jest.setup.next-api.js deleted file mode 100644 index f15d2ab..0000000 --- a/web/jest.setup.next-api.js +++ /dev/null @@ -1,78 +0,0 @@ -// Simple mock setup for Next.js API route testing -// Focus on mocking the essential parts without external dependencies - -// Mock the Next.js Request and Response classes directly -const mockRequest = { - json: async function() { - try { - return JSON.parse(this.body || '{}'); - } catch { - throw new Error('Invalid JSON'); - } - } -}; - -const mockResponse = { - json: function(data) { - return { - status: 200, - json: async () => data - }; - } -}; - -// Mock Next.js server module -jest.mock('next/server', () => { - class MockNextRequest { - constructor(url, { method = 'GET', body = '' } = {}) { - this.url = url; - this.method = method; - this.body = body; - this.nextUrl = new URL(url); - } - - async json() { - try { - return JSON.parse(this.body || '{}'); - } catch { - throw new Error('Invalid JSON'); - } - } - } - - class MockNextResponse { - constructor(body, init = {}) { - this.body = body; - this.status = init.status || 200; - this.headers = init.headers || {}; - } - - static json(data, init = {}) { - const response = new MockNextResponse(JSON.stringify(data), { - status: init.status || 200, - headers: { - 'content-type': 'application/json', - ...init.headers - } - }); - response._jsonData = data; - return response; - } - - async json() { - if (this._jsonData !== undefined) { - return this._jsonData; - } - try { - return JSON.parse(this.body); - } catch { - return {}; - } - } - } - - return { - NextRequest: MockNextRequest, - NextResponse: MockNextResponse - }; -}); \ No newline at end of file diff --git a/web/next.config.js b/web/next.config.js index e964b2e..556b3cb 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -4,6 +4,6 @@ const nextConfig = { // Enable experimental features if needed }, transpilePackages: ['@turn-based-mcp/shared'], -} +}; -module.exports = nextConfig +export default nextConfig; diff --git a/web/package.json b/web/package.json index 16a41f0..53da5d5 100644 --- a/web/package.json +++ b/web/package.json @@ -2,46 +2,52 @@ "name": "@turn-based-mcp/web", "version": "1.0.0", "private": true, + "type": "module", "scripts": { "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint", + "lint": "eslint .", + "lint:fix": "eslint . --fix", "type-check": "tsc --noEmit", "clean": "rm -rf .next", - "test": "jest", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage" + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "test:ui": "vitest --ui" }, "dependencies": { "@turn-based-mcp/shared": "file:../shared", "@types/sqlite3": "^5.1.0", "clsx": "^2.1.0", - "next": "^15.4.5", + "next": "^15.4.6", "react": "^19.1.1", "react-dom": "^19.1.1", "sqlite3": "^5.1.7", "tailwind-merge": "^3.3.1" }, "devDependencies": { - "@edge-runtime/jest-environment": "^4.0.0", + "@eslint/js": "^9.32.0", "@tailwindcss/postcss": "^4.1.0", "@testing-library/jest-dom": "^6.6.4", "@testing-library/react": "^16.3.0", "@testing-library/user-event": "^14.6.1", - "@types/jest": "^30.0.0", - "@types/node": "^24.1.0", + "@types/node": "^24.2.0", "@types/react": "^19.1.9", "@types/react-dom": "^19.1.7", + "@vitejs/plugin-react": "^5.0.0", + "@vitest/ui": "^3.2.4", "eslint": "^9.32.0", - "eslint-config-next": "^15.4.5", - "jest": "^30.0.5", - "jest-environment-jsdom": "^30.0.5", + "eslint-config-next": "^15.4.6", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "jsdom": "^26.1.0", "node-fetch": "^3.3.2", "postcss": "^8.4.40", "tailwindcss": "^4.1.11", - "ts-jest": "^29.4.0", - "typescript": "^5.5.0", - "undici": "^7.12.0" + "typescript": "^5.9.2", + "typescript-eslint": "^8.39.0", + "undici": "^7.13.0", + "vitest": "^3.2.4" } } diff --git a/web/postcss.config.js b/web/postcss.config.js index 52b9b4b..a34a3d5 100644 --- a/web/postcss.config.js +++ b/web/postcss.config.js @@ -1,5 +1,5 @@ -module.exports = { +export default { plugins: { '@tailwindcss/postcss': {}, }, -} +}; diff --git a/web/src/app/api/games/rock-paper-scissors/[id]/move/route.test.ts b/web/src/app/api/games/rock-paper-scissors/[id]/move/route.test.ts index d7aa7e4..90819a8 100644 --- a/web/src/app/api/games/rock-paper-scissors/[id]/move/route.test.ts +++ b/web/src/app/api/games/rock-paper-scissors/[id]/move/route.test.ts @@ -1,42 +1,43 @@ +import { vi, type MockedFunction, type MockedClass } from 'vitest' import { NextRequest } from 'next/server'; import type { GameSession, RPSGameState, RPSMove } from '@turn-based-mcp/shared'; // Mock dependencies BEFORE importing the route - use factory functions for proper setup -jest.mock('@turn-based-mcp/shared', () => { +vi.mock('@turn-based-mcp/shared', () => { const mockGame = { - getInitialState: jest.fn(), - validateMove: jest.fn(), - applyMove: jest.fn(), - checkGameEnd: jest.fn(), - getValidMoves: jest.fn() + getInitialState: vi.fn(), + validateMove: vi.fn(), + applyMove: vi.fn(), + checkGameEnd: vi.fn(), + getValidMoves: vi.fn() }; return { - ...jest.requireActual('@turn-based-mcp/shared'), - RockPaperScissorsGame: jest.fn(() => mockGame), + ...vi.importActual('@turn-based-mcp/shared'), + RockPaperScissorsGame: vi.fn(() => mockGame), __mockGameInstance: mockGame }; }); -jest.mock('../../../../../../lib/game-storage', () => ({ - getRPSGame: jest.fn(), - setRPSGame: jest.fn() +vi.mock('../../../../../../lib/game-storage', () => ({ + getRPSGame: vi.fn(), + setRPSGame: vi.fn() })); import { POST } from './route'; import { RockPaperScissorsGame } from '@turn-based-mcp/shared'; import { getRPSGame, setRPSGame } from '../../../../../../lib/game-storage'; -const mockGetRPSGame = getRPSGame as jest.MockedFunction; -const mockSetRPSGame = setRPSGame as jest.MockedFunction; +const mockGetRPSGame = getRPSGame as MockedFunction; +const mockSetRPSGame = setRPSGame as MockedFunction; // Get access to the mock game instance from the mocked module -const mockGame = (RockPaperScissorsGame as jest.MockedClass).mock.results[0]?.value || { - getInitialState: jest.fn(), - validateMove: jest.fn(), - applyMove: jest.fn(), - checkGameEnd: jest.fn(), - getValidMoves: jest.fn() +const mockGame = (RockPaperScissorsGame as MockedClass).mock.results[0]?.value || { + getInitialState: vi.fn(), + validateMove: vi.fn(), + applyMove: vi.fn(), + checkGameEnd: vi.fn(), + getValidMoves: vi.fn() }; describe('/api/games/rock-paper-scissors/[id]/move', () => { @@ -64,7 +65,7 @@ describe('/api/games/rock-paper-scissors/[id]/move', () => { let mockGameSession: GameSession; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); // Create fresh mock state for each test mockGameState = createMockGameState(); @@ -216,8 +217,6 @@ describe('/api/games/rock-paper-scissors/[id]/move', () => { }); it('should handle JSON parsing errors', async () => { - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); - const request = new NextRequest('http://localhost:3000/api/games/rock-paper-scissors/test-rps-1/move', { method: 'POST', body: 'invalid-json' @@ -229,36 +228,34 @@ describe('/api/games/rock-paper-scissors/[id]/move', () => { expect(response.status).toBe(500); expect(responseData).toEqual({ error: 'Failed to process move' }); - expect(consoleSpy).toHaveBeenCalledWith('Error processing move:', expect.any(Error)); - - consoleSpy.mockRestore(); }); - it('should handle storage errors', async () => { - const move: RPSMove = { choice: 'rock' }; - const storageError = new Error('Database connection failed'); - - mockGetRPSGame.mockRejectedValue(storageError); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + it('should handle storage errors gracefully', async () => { + mockGetRPSGame.mockResolvedValue(mockGameSession); + mockGame.validateMove.mockReturnValue(true); + mockGame.applyMove.mockReturnValue(mockGameState); + mockGame.checkGameEnd.mockReturnValue(null); + mockSetRPSGame.mockRejectedValue(new Error('Storage failed')); - const request = new NextRequest('http://localhost:3000/api/games/rock-paper-scissors/test-rps-1/move', { + const request = new NextRequest('http://localhost/api/games/rock-paper-scissors/test-game/move', { method: 'POST', - body: JSON.stringify({ move, playerId: 'player1' }) + body: JSON.stringify({ + playerId: 'player1', + move: { choice: 'rock' } + }) }); - const params = Promise.resolve({ id: 'test-rps-1' }); + const params = Promise.resolve({ id: 'test-game' }); const response = await POST(request, { params }); const responseData = await response.json(); expect(response.status).toBe(500); expect(responseData).toEqual({ error: 'Failed to process move' }); - expect(consoleSpy).toHaveBeenCalledWith('Error processing move:', storageError); - - consoleSpy.mockRestore(); }); it('should handle missing request body fields', async () => { mockGetRPSGame.mockResolvedValue(mockGameSession); + mockGame.validateMove.mockReturnValue(false); const request = new NextRequest('http://localhost:3000/api/games/rock-paper-scissors/test-rps-1/move', { method: 'POST', @@ -276,7 +273,7 @@ describe('/api/games/rock-paper-scissors/[id]/move', () => { const choices: Array<'rock' | 'paper' | 'scissors'> = ['rock', 'paper', 'scissors']; for (const choice of choices) { - jest.clearAllMocks(); + vi.clearAllMocks(); // Create fresh state for each iteration to avoid mutation const freshMockGameState = createMockGameState(); diff --git a/web/src/app/api/games/rock-paper-scissors/mcp/route.test.ts b/web/src/app/api/games/rock-paper-scissors/mcp/route.test.ts index 1b43a27..7c19984 100644 --- a/web/src/app/api/games/rock-paper-scissors/mcp/route.test.ts +++ b/web/src/app/api/games/rock-paper-scissors/mcp/route.test.ts @@ -1,16 +1,17 @@ +import { vi, type MockedFunction } from 'vitest' import { GET } from './route' import { getAllRPSGames } from '../../../../../lib/game-storage' import type { GameSession } from '@turn-based-mcp/shared' import type { RPSGameState } from '@turn-based-mcp/shared' // Mock the game storage -jest.mock('../../../../../lib/game-storage') +vi.mock('../../../../../lib/game-storage') -const mockGetAllRPSGames = getAllRPSGames as jest.MockedFunction +const mockGetAllRPSGames = getAllRPSGames as MockedFunction describe('/api/games/rock-paper-scissors/mcp', () => { beforeEach(() => { - jest.clearAllMocks() + vi.clearAllMocks() }) it('should sanitize current round player choices when round is in progress', async () => { diff --git a/web/src/app/api/games/rock-paper-scissors/route.test.ts b/web/src/app/api/games/rock-paper-scissors/route.test.ts index 091fe32..c0a1151 100644 --- a/web/src/app/api/games/rock-paper-scissors/route.test.ts +++ b/web/src/app/api/games/rock-paper-scissors/route.test.ts @@ -1,72 +1,47 @@ +import { vi } from 'vitest' import { NextRequest } from 'next/server'; import type { GameSession, RPSGameState } from '@turn-based-mcp/shared'; - -// Mock dependencies BEFORE importing the route - use factory functions for proper setup -jest.mock('@turn-based-mcp/shared', () => { - const mockGame = { - getInitialState: jest.fn(), - validateMove: jest.fn(), - applyMove: jest.fn(), - checkGameEnd: jest.fn(), - getValidMoves: jest.fn() - }; - - return { - ...jest.requireActual('@turn-based-mcp/shared'), - RockPaperScissorsGame: jest.fn(() => mockGame), - __mockGameInstance: mockGame - }; -}); - -jest.mock('../../../../lib/game-storage'); - -// Import the mocked classes -import { RockPaperScissorsGame } from '@turn-based-mcp/shared'; +import { createRPSTestState } from '../../../../test-utils/common-test-data'; + +// Use vi.hoisted() to ensure the mock object is available during hoisting +const mockGame = vi.hoisted(() => ({ + getInitialState: vi.fn(), + validateMove: vi.fn(), + applyMove: vi.fn(), + checkGameEnd: vi.fn(), + getValidMoves: vi.fn() +})); + +// Mock dependencies BEFORE importing the route +vi.mock('@turn-based-mcp/shared', () => ({ + ...vi.importActual('@turn-based-mcp/shared'), + RockPaperScissorsGame: vi.fn().mockImplementation(() => mockGame) +})); + +vi.mock('../../../../lib/game-storage', () => ({ + getRPSGame: vi.fn(), + setRPSGame: vi.fn(), + getAllRPSGames: vi.fn() +})); + +// Import the mocked storage import * as gameStorage from '../../../../lib/game-storage'; // Now import the route AFTER the mocks are set up import { GET, POST } from './route'; -const mockGameStorage = gameStorage as jest.Mocked; - -// Get access to the mock game instance from the mocked module -const mockGame = (RockPaperScissorsGame as jest.MockedClass).mock.results[0]?.value || { - getInitialState: jest.fn(), - validateMove: jest.fn(), - applyMove: jest.fn(), - checkGameEnd: jest.fn(), - getValidMoves: jest.fn() -}; +const mockGameStorage = vi.mocked(gameStorage); describe('/api/games/rock-paper-scissors', () => { - // Create the mock game state at module level - const createMockGameState = (): RPSGameState => ({ - id: 'test-rps-1', - players: [ - { id: 'player1' as const, name: 'Player', isAI: false }, - { id: 'ai' as const, name: 'AI', isAI: true } - ], - currentPlayerId: 'player1' as const, - status: 'playing' as const, - createdAt: new Date('2024-01-01T10:00:00Z'), - updatedAt: new Date('2024-01-01T10:00:00Z'), - rounds: [], - currentRound: 0, - scores: { - player1: 0, - ai: 0 - }, - maxRounds: 3 - }); - + // Use shared test data factory to reduce duplication let mockGameState: RPSGameState; let mockGameSession: GameSession; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); - // Create fresh mock state for each test - mockGameState = createMockGameState(); + // Create fresh test data for each test using shared factory + mockGameState = createRPSTestState(); mockGameSession = { gameState: mockGameState, gameType: 'rock-paper-scissors' as const, @@ -93,7 +68,7 @@ describe('/api/games/rock-paper-scissors', () => { expect(mockGame.getInitialState).toHaveBeenCalledWith([ { id: 'player1', name: 'TestPlayer', isAI: false }, { id: 'ai', name: 'AI', isAI: true } - ]); + ], { maxRounds: undefined }); expect(mockGameStorage.setRPSGame).toHaveBeenCalledWith( mockGameState.id, expect.objectContaining({ @@ -123,7 +98,7 @@ describe('/api/games/rock-paper-scissors', () => { expect(mockGame.getInitialState).toHaveBeenCalledWith([ { id: 'player1', name: 'Player', isAI: false }, { id: 'ai', name: 'AI', isAI: true } - ]); + ], { maxRounds: undefined }); }); it('should handle empty request body', async () => { @@ -140,13 +115,12 @@ describe('/api/games/rock-paper-scissors', () => { expect(mockGame.getInitialState).toHaveBeenCalledWith([ { id: 'player1', name: 'Player', isAI: false }, { id: 'ai', name: 'AI', isAI: true } - ]); + ], { maxRounds: undefined }); }); it('should handle storage errors', async () => { const storageError = new Error('Storage failed'); mockGameStorage.setRPSGame.mockRejectedValue(storageError); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); const request = new NextRequest('http://localhost:3000/api/games/rock-paper-scissors', { method: 'POST', @@ -158,14 +132,9 @@ describe('/api/games/rock-paper-scissors', () => { expect(response.status).toBe(500); expect(responseData).toEqual({ error: 'Failed to create game' }); - expect(consoleSpy).toHaveBeenCalledWith('Error creating game:', storageError); - - consoleSpy.mockRestore(); }); it('should handle JSON parsing errors', async () => { - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); - const request = new NextRequest('http://localhost:3000/api/games/rock-paper-scissors', { method: 'POST', body: 'invalid-json' @@ -176,9 +145,6 @@ describe('/api/games/rock-paper-scissors', () => { expect(response.status).toBe(500); expect(responseData).toEqual({ error: 'Failed to create game' }); - expect(consoleSpy).toHaveBeenCalledWith('Error creating game:', expect.any(Error)); - - consoleSpy.mockRestore(); }); }); diff --git a/web/src/app/api/games/rock-paper-scissors/route.ts b/web/src/app/api/games/rock-paper-scissors/route.ts index 2ab3a23..3e2cabb 100644 --- a/web/src/app/api/games/rock-paper-scissors/route.ts +++ b/web/src/app/api/games/rock-paper-scissors/route.ts @@ -8,14 +8,14 @@ const rpsGame = new RockPaperScissorsGame() export async function POST(request: NextRequest) { try { - const { playerName, gameId, aiDifficulty } = await request.json() + const { playerName, gameId, difficulty, maxRounds } = await request.json() const players: Player[] = [ { id: 'player1', name: playerName || 'Player', isAI: false }, { id: 'ai', name: 'AI', isAI: true } ] - const gameState = rpsGame.getInitialState(players) + const gameState = rpsGame.getInitialState(players, { maxRounds }) // Use custom gameId if provided if (gameId) { @@ -26,7 +26,7 @@ export async function POST(request: NextRequest) { gameState, gameType: 'rock-paper-scissors', history: [], - aiDifficulty: aiDifficulty || 'medium' + difficulty: difficulty || 'medium' } await setRPSGame(gameState.id, gameSession) diff --git a/web/src/app/api/games/tic-tac-toe/[id]/move/route.test.ts b/web/src/app/api/games/tic-tac-toe/[id]/move/route.test.ts index 7389679..b3e0327 100644 --- a/web/src/app/api/games/tic-tac-toe/[id]/move/route.test.ts +++ b/web/src/app/api/games/tic-tac-toe/[id]/move/route.test.ts @@ -1,21 +1,22 @@ +import { vi, type MockedFunction, type MockedClass } from 'vitest' import { NextRequest } from 'next/server'; import type { GameSession, TicTacToeGameState, TicTacToeMove } from '@turn-based-mcp/shared'; // Mock dependencies BEFORE importing the route - use factory functions for proper setup -jest.mock('@turn-based-mcp/shared', () => { +vi.mock('@turn-based-mcp/shared', () => { const mockGame = { - getInitialState: jest.fn(), - validateMove: jest.fn(), - applyMove: jest.fn(), - checkGameEnd: jest.fn(), - getValidMoves: jest.fn() + getInitialState: vi.fn(), + validateMove: vi.fn(), + applyMove: vi.fn(), + checkGameEnd: vi.fn(), + getValidMoves: vi.fn() }; return { - ...jest.requireActual('@turn-based-mcp/shared'), - TicTacToeGame: jest.fn(() => mockGame), - getTicTacToeGame: jest.fn(), - setTicTacToeGame: jest.fn(), + ...vi.importActual('@turn-based-mcp/shared'), + TicTacToeGame: vi.fn(() => mockGame), + getTicTacToeGame: vi.fn(), + setTicTacToeGame: vi.fn(), __mockGameInstance: mockGame }; }); @@ -23,16 +24,16 @@ jest.mock('@turn-based-mcp/shared', () => { import { POST } from './route'; import { TicTacToeGame, getTicTacToeGame, setTicTacToeGame } from '@turn-based-mcp/shared'; -const mockGetTicTacToeGame = getTicTacToeGame as jest.MockedFunction; -const mockSetTicTacToeGame = setTicTacToeGame as jest.MockedFunction; +const mockGetTicTacToeGame = getTicTacToeGame as MockedFunction; +const mockSetTicTacToeGame = setTicTacToeGame as MockedFunction; // Get access to the mock game instance from the mocked module -const mockGame = (TicTacToeGame as jest.MockedClass).mock.results[0]?.value || { - getInitialState: jest.fn(), - validateMove: jest.fn(), - applyMove: jest.fn(), - checkGameEnd: jest.fn(), - getValidMoves: jest.fn() +const mockGame = (TicTacToeGame as MockedClass).mock.results[0]?.value || { + getInitialState: vi.fn(), + validateMove: vi.fn(), + applyMove: vi.fn(), + checkGameEnd: vi.fn(), + getValidMoves: vi.fn() }; describe('/api/games/tic-tac-toe/[id]/move', () => { @@ -62,7 +63,7 @@ describe('/api/games/tic-tac-toe/[id]/move', () => { let mockGameSession: GameSession; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); // Create fresh mock state for each test mockGameState = createMockGameState(); @@ -215,8 +216,6 @@ describe('/api/games/tic-tac-toe/[id]/move', () => { }); it('should handle JSON parsing errors', async () => { - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); - const request = new NextRequest('http://localhost:3000/api/games/tic-tac-toe/test-game-1/move', { method: 'POST', body: 'invalid-json' @@ -228,21 +227,21 @@ describe('/api/games/tic-tac-toe/[id]/move', () => { expect(response.status).toBe(500); expect(responseData).toEqual({ error: 'Failed to process move' }); - expect(consoleSpy).toHaveBeenCalledWith('Error processing move:', expect.any(Error)); - - consoleSpy.mockRestore(); }); - it('should handle storage errors', async () => { - const move: TicTacToeMove = { row: 0, col: 0 }; - const storageError = new Error('Database connection failed'); - - mockGetTicTacToeGame.mockRejectedValue(storageError); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + it('should handle storage errors gracefully', async () => { + mockGetTicTacToeGame.mockResolvedValue(mockGameSession); + mockGame.validateMove.mockReturnValue(true); + mockGame.applyMove.mockReturnValue(mockGameState); + mockGame.checkGameEnd.mockReturnValue(null); + mockSetTicTacToeGame.mockRejectedValue(new Error('Storage failed')); const request = new NextRequest('http://localhost:3000/api/games/tic-tac-toe/test-game-1/move', { method: 'POST', - body: JSON.stringify({ move, playerId: 'player1' }) + body: JSON.stringify({ + playerId: 'player1', + move: { row: 0, col: 0 } + }) }); const params = Promise.resolve({ id: 'test-game-1' }); @@ -251,13 +250,11 @@ describe('/api/games/tic-tac-toe/[id]/move', () => { expect(response.status).toBe(500); expect(responseData).toEqual({ error: 'Failed to process move' }); - expect(consoleSpy).toHaveBeenCalledWith('Error processing move:', storageError); - - consoleSpy.mockRestore(); }); it('should handle missing request body fields', async () => { mockGetTicTacToeGame.mockResolvedValue(mockGameSession); + mockGame.validateMove.mockReturnValue(false); const request = new NextRequest('http://localhost:3000/api/games/tic-tac-toe/test-game-1/move', { method: 'POST', diff --git a/web/src/app/api/games/tic-tac-toe/route.test.ts b/web/src/app/api/games/tic-tac-toe/route.test.ts index c297ff5..b28bd43 100644 --- a/web/src/app/api/games/tic-tac-toe/route.test.ts +++ b/web/src/app/api/games/tic-tac-toe/route.test.ts @@ -1,65 +1,40 @@ +import { vi } from 'vitest' import { NextRequest } from 'next/server'; import type { GameSession, TicTacToeGameState } from '@turn-based-mcp/shared'; - -// Mock dependencies BEFORE importing the route - use factory functions for proper setup -jest.mock('@turn-based-mcp/shared', () => { - const mockGame = { - getInitialState: jest.fn(), - validateMove: jest.fn(), - applyMove: jest.fn(), - checkGameEnd: jest.fn(), - getValidMoves: jest.fn() - }; - - return { - ...jest.requireActual('@turn-based-mcp/shared'), - TicTacToeGame: jest.fn(() => mockGame), - __mockGameInstance: mockGame - }; -}); - -jest.mock('../../../../lib/game-storage'); - -// Import the mocked classes -import { TicTacToeGame } from '@turn-based-mcp/shared'; +import { createTicTacToeTestState } from '../../../../test-utils/common-test-data'; + +// Use vi.hoisted() to ensure the mock object is available during hoisting +const mockGame = vi.hoisted(() => ({ + getInitialState: vi.fn(), + validateMove: vi.fn(), + applyMove: vi.fn(), + checkGameEnd: vi.fn(), + getValidMoves: vi.fn() +})); + +// Mock dependencies BEFORE importing the route +vi.mock('@turn-based-mcp/shared', () => ({ + ...vi.importActual('@turn-based-mcp/shared'), + TicTacToeGame: vi.fn().mockImplementation(() => mockGame) +})); + +vi.mock('../../../../lib/game-storage', () => ({ + getTicTacToeGame: vi.fn(), + setTicTacToeGame: vi.fn(), + getAllTicTacToeGames: vi.fn() +})); + +// Import the mocked storage import * as gameStorage from '../../../../lib/game-storage'; // Now import the route AFTER the mocks are set up import { GET, POST } from './route'; -const mockGameStorage = gameStorage as jest.Mocked; - -// Get access to the mock game instance from the mocked module -const mockGame = (TicTacToeGame as jest.MockedClass).mock.results[0]?.value || { - getInitialState: jest.fn(), - validateMove: jest.fn(), - applyMove: jest.fn(), - checkGameEnd: jest.fn(), - getValidMoves: jest.fn() -}; +const mockGameStorage = vi.mocked(gameStorage); describe('/api/games/tic-tac-toe', () => { - // Create the mock game state at module level - const mockGameState: TicTacToeGameState = { - id: 'test-game-1', - players: [ - { id: 'player1' as const, name: 'Player', isAI: false }, - { id: 'ai' as const, name: 'AI', isAI: true } - ], - currentPlayerId: 'player1' as const, - status: 'playing' as const, - createdAt: new Date('2024-01-01T10:00:00Z'), - updatedAt: new Date('2024-01-01T10:00:00Z'), - board: [ - [null, null, null], - [null, null, null], - [null, null, null] - ], - playerSymbols: { - player1: 'X' as const, - ai: 'O' as const - } - }; + // Use shared test data factory to reduce duplication + const mockGameState = createTicTacToeTestState(); const mockGameSession: GameSession = { gameState: mockGameState, @@ -68,14 +43,18 @@ describe('/api/games/tic-tac-toe', () => { }; beforeEach(() => { - // Don't clear all mocks as it clears the return values - // Instead, just reset the call history + // Reset mock implementations and call history mockGame.getInitialState.mockClear(); mockGame.validateMove.mockClear(); mockGame.applyMove.mockClear(); mockGame.checkGameEnd.mockClear(); mockGame.getValidMoves.mockClear(); + // Reset storage mocks + mockGameStorage.getTicTacToeGame.mockClear(); + mockGameStorage.setTicTacToeGame.mockClear(); + mockGameStorage.getAllTicTacToeGames.mockClear(); + // Reset mock implementations with defaults mockGame.getInitialState.mockReturnValue(mockGameState); mockGame.validateMove.mockReturnValue(true); @@ -100,7 +79,7 @@ describe('/api/games/tic-tac-toe', () => { expect(mockGame.getInitialState).toHaveBeenCalledWith([ { id: 'player1', name: 'TestPlayer', isAI: false }, { id: 'ai', name: 'AI', isAI: true } - ]); + ], { firstPlayerId: 'player1' }); expect(mockGameStorage.setTicTacToeGame).toHaveBeenCalledWith( mockGameState.id, expect.objectContaining({ @@ -130,7 +109,7 @@ describe('/api/games/tic-tac-toe', () => { expect(mockGame.getInitialState).toHaveBeenCalledWith([ { id: 'player1', name: 'Player', isAI: false }, { id: 'ai', name: 'AI', isAI: true } - ]); + ], { firstPlayerId: 'player1' }); }); it('should handle empty request body', async () => { @@ -147,13 +126,12 @@ describe('/api/games/tic-tac-toe', () => { expect(mockGame.getInitialState).toHaveBeenCalledWith([ { id: 'player1', name: 'Player', isAI: false }, { id: 'ai', name: 'AI', isAI: true } - ]); + ], { firstPlayerId: 'player1' }); }); it('should handle storage errors', async () => { const storageError = new Error('Storage failed'); mockGameStorage.setTicTacToeGame.mockRejectedValue(storageError); - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); const request = new NextRequest('http://localhost:3000/api/games/tic-tac-toe', { method: 'POST', @@ -165,14 +143,9 @@ describe('/api/games/tic-tac-toe', () => { expect(response.status).toBe(500); expect(responseData).toEqual({ error: 'Failed to create game' }); - expect(consoleSpy).toHaveBeenCalledWith('Error creating game:', storageError); - - consoleSpy.mockRestore(); }); it('should handle JSON parsing errors', async () => { - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); - const request = new NextRequest('http://localhost:3000/api/games/tic-tac-toe', { method: 'POST', body: 'invalid-json' @@ -183,9 +156,6 @@ describe('/api/games/tic-tac-toe', () => { expect(response.status).toBe(500); expect(responseData).toEqual({ error: 'Failed to create game' }); - expect(consoleSpy).toHaveBeenCalledWith('Error creating game:', expect.any(Error)); - - consoleSpy.mockRestore(); }); }); diff --git a/web/src/app/api/games/tic-tac-toe/route.ts b/web/src/app/api/games/tic-tac-toe/route.ts index 3188c40..2c0e79f 100644 --- a/web/src/app/api/games/tic-tac-toe/route.ts +++ b/web/src/app/api/games/tic-tac-toe/route.ts @@ -8,14 +8,18 @@ const ticTacToeGame = new TicTacToeGame() export async function POST(request: NextRequest) { try { - const { playerName, gameId, aiDifficulty } = await request.json() + const { playerName, gameId, difficulty, playerSymbol } = await request.json() const players: Player[] = [ { id: 'player1', name: playerName || 'Player', isAI: false }, { id: 'ai', name: 'AI', isAI: true } ] - const gameState = ticTacToeGame.getInitialState(players) + const options: { firstPlayerId: string } = { + firstPlayerId: playerSymbol === 'O' ? 'ai' : 'player1' + }; + + const gameState = ticTacToeGame.getInitialState(players, options) // Use custom gameId if provided if (gameId) { @@ -26,7 +30,7 @@ export async function POST(request: NextRequest) { gameState, gameType: 'tic-tac-toe', history: [], - aiDifficulty: aiDifficulty || 'medium' + difficulty: difficulty || 'medium' } await setTicTacToeGame(gameState.id, gameSession) diff --git a/web/src/app/games/rock-paper-scissors/page.tsx b/web/src/app/games/rock-paper-scissors/page.tsx index 8984891..14739e2 100644 --- a/web/src/app/games/rock-paper-scissors/page.tsx +++ b/web/src/app/games/rock-paper-scissors/page.tsx @@ -5,7 +5,7 @@ import { RPSGameBoard } from '../../../components/games/RPSGameBoard' import { GameInfoPanel } from '../../../components/games/GameInfoPanel' import { GameContainer, GameControls, ConfirmationModal } from '../../../components/ui' import { MCPAssistantPanel } from '../../../components/shared' -import type { RPSGameState, RPSMove } from '@turn-based-mcp/shared' +import type { RPSGameState, RPSMove, Difficulty } from '@turn-based-mcp/shared' import type { GameSession } from '@turn-based-mcp/shared' export default function RockPaperScissorsPage() { @@ -16,7 +16,8 @@ export default function RockPaperScissorsPage() { const [availableGames, setAvailableGames] = useState[]>([]) const [showCreateForm, setShowCreateForm] = useState(false) const [showJoinForm, setShowJoinForm] = useState(false) - const [aiDifficulty, setAiDifficulty] = useState<'easy' | 'medium' | 'hard'>('medium') + const [aiDifficulty, setAiDifficulty] = useState('medium') + const [maxRounds, setMaxRounds] = useState(3) const [gamesToShow, setGamesToShow] = useState(5) const [showDeleteModal, setShowDeleteModal] = useState(false) const [gameToDelete, setGameToDelete] = useState(null) @@ -79,9 +80,10 @@ export default function RockPaperScissorsPage() { setError(null) try { - const body: { playerName: string; gameId?: string; aiDifficulty: string } = { + const body: { playerName: string; gameId?: string; difficulty: string; maxRounds: number } = { playerName: 'Player', - aiDifficulty + difficulty: aiDifficulty, + maxRounds } if (customGameId) { body.gameId = customGameId @@ -237,7 +239,7 @@ export default function RockPaperScissorsPage() { <>
@@ -280,7 +282,7 @@ export default function RockPaperScissorsPage() { <> Round: {game.gameState.currentRound + 1}/{game.gameState.maxRounds} • Turn: {game.gameState.currentPlayerId === 'player1' ? 'Player' : 'AI'} • - Difficulty: {game.aiDifficulty || 'medium'} + Difficulty: {game.difficulty || 'medium'}
@@ -431,7 +433,7 @@ export default function RockPaperScissorsPage() { +
+ + +
} // Empty sidebar for setup error={error} diff --git a/web/src/app/games/tic-tac-toe/page.tsx b/web/src/app/games/tic-tac-toe/page.tsx index e8694b5..2f4a754 100644 --- a/web/src/app/games/tic-tac-toe/page.tsx +++ b/web/src/app/games/tic-tac-toe/page.tsx @@ -5,7 +5,7 @@ import { TicTacToeBoard } from '../../../components/games/TicTacToeBoard' import { GameInfoPanel } from '../../../components/games/GameInfoPanel' import { GameContainer, GameControls, ConfirmationModal } from '../../../components/ui' import { MCPAssistantPanel } from '../../../components/shared' -import type { TicTacToeGameState, TicTacToeMove } from '@turn-based-mcp/shared' +import type { TicTacToeGameState, TicTacToeMove, Difficulty } from '@turn-based-mcp/shared' import type { GameSession } from '@turn-based-mcp/shared' export default function TicTacToePage() { @@ -16,7 +16,8 @@ export default function TicTacToePage() { const [availableGames, setAvailableGames] = useState[]>([]) const [showCreateForm, setShowCreateForm] = useState(false) const [showJoinForm, setShowJoinForm] = useState(false) - const [aiDifficulty, setAiDifficulty] = useState<'easy' | 'medium' | 'hard'>('medium') + const [aiDifficulty, setAiDifficulty] = useState('medium') + const [playerSymbol, setPlayerSymbol] = useState<'X' | 'O'>('X') const [gamesToShow, setGamesToShow] = useState(5) const [showDeleteModal, setShowDeleteModal] = useState(false) const [gameToDelete, setGameToDelete] = useState(null) @@ -79,9 +80,10 @@ export default function TicTacToePage() { setError(null) try { - const body: { playerName: string; gameId?: string; aiDifficulty: string } = { + const body: { playerName: string; gameId?: string; difficulty: string; playerSymbol: string } = { playerName: 'Player', - aiDifficulty + difficulty: aiDifficulty, + playerSymbol } if (customGameId) { body.gameId = customGameId @@ -237,7 +239,7 @@ export default function TicTacToePage() { <> - {game.aiDifficulty || 'medium'} + {game.difficulty || 'medium'} @@ -425,7 +427,7 @@ export default function TicTacToePage() { +
+ + +