diff --git a/.github/workflows/qodana_code_quality.yml b/.github/workflows/qodana_code_quality.yml index 0988492..6afdef3 100644 --- a/.github/workflows/qodana_code_quality.yml +++ b/.github/workflows/qodana_code_quality.yml @@ -17,12 +17,27 @@ jobs: steps: - uses: actions/checkout@v3 with: - ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit - fetch-depth: 0 # a full history is required for pull request analysis + ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit + fetch-depth: 0 # a full history is required for pull request analysis + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: '20' # Specify the Node.js version you want to use + cache: 'yarn' + + - name: Set up FontAwesome NPM registry + run: ./fontawesome-npmrc.sh + env: + FONTAWESOME_KEY: ${{ secrets.FONTAWESOME_KEY }} + + - name: Install dependencies + run: yarn install --frozen-lockfile + - name: 'Qodana Scan' uses: JetBrains/qodana-action@v2024.2 with: pr-mode: false env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN_1177090119 }} - QODANA_ENDPOINT: 'https://qodana.cloud' \ No newline at end of file + QODANA_ENDPOINT: 'https://qodana.cloud' diff --git a/package.json b/package.json index 12ed420..2b19344 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "@types/react-window": "^1.8.8", "@types/uuid": "^10.0.0", "babel-jest": "^29.7.0", - "eslint": "^9.8.0", + "eslint": "^9.14.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "2.31.0", "eslint-plugin-jsx-a11y": "6.7.1", diff --git a/src/app/app.tsx b/src/app/app.tsx index 524a6b8..21692f1 100644 --- a/src/app/app.tsx +++ b/src/app/app.tsx @@ -1,6 +1,6 @@ import React, { useRef, ReactElement, ReactNode, useEffect, useState } from 'react'; import { ThemeProvider } from '@mui/material/styles'; -import { BrowserRouter as Router, useLocation, useNavigate, useRoutes } from 'react-router-dom'; +import { BrowserRouter as Router, useLocation, useRoutes } from 'react-router-dom'; import AboutScreen from '@/components/screens/AboutScreen'; import HomeIcon from '@mui/icons-material/Home'; import BookIcon from '@mui/icons-material/Book'; @@ -89,7 +89,7 @@ function AppRoutes() { }); } - const routes = useRoutes([ + return useRoutes([ { path: '/', element: , @@ -100,8 +100,6 @@ function AppRoutes() { })), }, ]); - - return routes; } export function App() { diff --git a/src/components/screens/UtilsScreen.tsx b/src/components/screens/UtilsScreen.tsx index cfa8181..ecc12a8 100644 --- a/src/components/screens/UtilsScreen.tsx +++ b/src/components/screens/UtilsScreen.tsx @@ -28,8 +28,7 @@ export const UtilsScreen: FC = () => { ) { return prevState; } - const newGame = rebuildGameTimeHistory(prevState); - return newGame; + return rebuildGameTimeHistory(prevState); }); }; diff --git a/src/game/__tests__/dominion-lib-log-calculatePausedTime.spec.ts b/src/game/__tests__/dominion-lib-log-calculatePausedTime.spec.ts new file mode 100644 index 0000000..d7d3dca --- /dev/null +++ b/src/game/__tests__/dominion-lib-log-calculatePausedTime.spec.ts @@ -0,0 +1,117 @@ +import { calculatePausedTime } from '@/game/dominion-lib-log'; +import { ILogEntry } from '@/game/interfaces/log-entry'; +import { GameLogAction } from '@/game/enumerations/game-log-action'; +import { createMockLog } from '@/__fixtures__/dominion-lib-fixtures'; + +describe('calculatePausedTime', () => { + it('should return 0 if logEntries is empty', () => { + const logEntries: ILogEntry[] = []; + const endTime = new Date(); + expect(calculatePausedTime(logEntries, 0, endTime)).toBe(0); + }); + + it('should return 0 if no pause or save/load actions are present', () => { + const logEntries: ILogEntry[] = [ + createMockLog({ + timestamp: new Date('2023-01-01T00:00:00Z'), + action: GameLogAction.START_GAME, + turn: 1, + }), + createMockLog({ + timestamp: new Date('2023-01-01T01:00:00Z'), + action: GameLogAction.NEXT_TURN, + turn: 2, + }), + ]; + const endTime = new Date('2023-01-01T02:00:00Z'); + expect(calculatePausedTime(logEntries, 0, endTime)).toBe(0); + }); + + it('should calculate paused time correctly for pause/unpause actions', () => { + const logEntries: ILogEntry[] = [ + createMockLog({ + timestamp: new Date('2023-01-01T00:00:00Z'), + action: GameLogAction.START_GAME, + turn: 1, + }), + createMockLog({ + timestamp: new Date('2023-01-01T01:00:00Z'), + action: GameLogAction.PAUSE, + turn: 1, + }), + createMockLog({ + timestamp: new Date('2023-01-01T01:30:00Z'), + action: GameLogAction.UNPAUSE, + turn: 1, + }), + ]; + const endTime = new Date('2023-01-01T02:00:00Z'); + expect(calculatePausedTime(logEntries, 0, endTime)).toBe(30 * 60 * 1000); // 30 minutes in milliseconds + }); + + it('should calculate paused time correctly for save/load actions', () => { + const logEntries: ILogEntry[] = [ + createMockLog({ + timestamp: new Date('2023-01-01T00:00:00Z'), + action: GameLogAction.START_GAME, + turn: 1, + }), + createMockLog({ + timestamp: new Date('2023-01-01T01:00:00Z'), + action: GameLogAction.SAVE_GAME, + turn: 1, + }), + createMockLog({ + timestamp: new Date('2023-01-01T01:30:00Z'), + action: GameLogAction.LOAD_GAME, + turn: 1, + }), + ]; + const endTime = new Date('2023-01-01T02:00:00Z'); + expect(calculatePausedTime(logEntries, 0, endTime)).toBe(30 * 60 * 1000); // 30 minutes in milliseconds + }); + + it('should handle case where end time is during a pause', () => { + const logEntries: ILogEntry[] = [ + createMockLog({ + timestamp: new Date('2023-01-01T00:00:00Z'), + action: GameLogAction.START_GAME, + turn: 1, + }), + createMockLog({ + timestamp: new Date('2023-01-01T01:00:00Z'), + action: GameLogAction.PAUSE, + turn: 1, + }), + ]; + const endTime = new Date('2023-01-01T01:30:00Z'); + expect(calculatePausedTime(logEntries, 0, endTime)).toBe(30 * 60 * 1000); // 30 minutes in milliseconds + }); + + it('should handle case where end time is after a pause/unpause cycle', () => { + const logEntries: ILogEntry[] = [ + createMockLog({ + timestamp: new Date('2023-01-01T00:00:00Z'), + action: GameLogAction.START_GAME, + turn: 1, + }), + createMockLog({ + timestamp: new Date('2023-01-01T01:00:00Z'), + action: GameLogAction.PAUSE, + turn: 1, + }), + createMockLog({ + timestamp: new Date('2023-01-01T01:30:00Z'), + action: GameLogAction.UNPAUSE, + turn: 1, + }), + createMockLog({ + timestamp: new Date('2023-01-01T02:00:00Z'), + action: GameLogAction.PAUSE, + turn: 1, + }), + ]; + const endTime = new Date('2023-01-01T02:30:00Z'); + expect(calculatePausedTime(logEntries, 0, endTime)).toBe(60 * 60 * 1000); // 1 hour in milliseconds + }); +}); diff --git a/src/game/__tests__/dominion-lib-undo-removeTargetAndLinkedActions.spec.ts b/src/game/__tests__/dominion-lib-undo-removeTargetAndLinkedActions.spec.ts index 6a490b2..0e79d82 100644 --- a/src/game/__tests__/dominion-lib-undo-removeTargetAndLinkedActions.spec.ts +++ b/src/game/__tests__/dominion-lib-undo-removeTargetAndLinkedActions.spec.ts @@ -1,6 +1,5 @@ import { removeTargetAndLinkedActions } from '@/game/dominion-lib-undo-helpers'; import { IGame } from '@/game/interfaces/game'; -import { ILogEntry } from '@/game/interfaces/log-entry'; import { GameLogAction } from '@/game/enumerations/game-log-action'; import { createMockGame, createMockLog } from '@/__fixtures__/dominion-lib-fixtures'; diff --git a/src/game/dominion-lib-log.ts b/src/game/dominion-lib-log.ts index b2c02c1..d288e43 100644 --- a/src/game/dominion-lib-log.ts +++ b/src/game/dominion-lib-log.ts @@ -137,6 +137,7 @@ export function fieldSubfieldToGameLogAction( /** * Transform a log entry to a string. * @param entry - The log entry + * @param useFutureTense - Whether to use future tense for the action * @returns The string representation of the log entry */ export function logEntryToString(entry: ILogEntry, useFutureTense = false): string { @@ -261,9 +262,7 @@ export function getTimeSpanFromLastAction(log: ILogEntry[], eventTime: Date): nu } const lastActionTime = new Date(log[log.length - 1].timestamp); - const timeSpan = eventTime.getTime() - lastActionTime.getTime(); - - return timeSpan; + return eventTime.getTime() - lastActionTime.getTime(); } /** @@ -280,8 +279,7 @@ export function calculateAverageTurnDuration(turnDurations: ITurnDuration[]): nu return accumulator + turn.duration; }, 0); - const averageDuration = totalDuration / turnDurations.length; - return averageDuration; + return totalDuration / turnDurations.length; } /** @@ -304,30 +302,30 @@ export function calculateAverageTurnDurationForPlayer( return accumulator + turn.duration; }, 0); - const averageDuration = totalDuration / playerTurns.length; - return averageDuration; + return totalDuration / playerTurns.length; } /** - * Calculates the duration of the current turn from the last NEXT_TURN or START_GAME up to the current time. - * Subtracts the time between any SAVE_GAME and the immediately following LOAD_GAME within the turn. + * Calculates the total number of actions performed by a specific player. * @param logEntries - The game log entries. - * @param currentTime - The current time. - * @returns The duration of the current turn in milliseconds. + * @param startIndex - The index of the first log entry to consider. + * @param endTime - The end time up to which the actions are counted. + * @returns The total number of actions performed by the player. */ -export function calculateCurrentTurnDuration(logEntries: ILogEntry[], currentTime: Date): number { - if (logEntries.length === 0) { - return 0; - } - const currentTurn = logEntries[logEntries.length - 1].turn; - const turnStartEntry = getTurnStartEntry(logEntries, currentTurn); - +export function calculatePausedTime( + logEntries: ILogEntry[], + startIndex: number, + endTime: Date +): number { let totalPausedTime = 0; let lastSaveTime: number | null = null; let pauseStartTime: number | null = null; - for (let i = logEntries.indexOf(turnStartEntry); i < logEntries.length; i++) { + for (let i = startIndex; i < logEntries.length; i++) { const entry = logEntries[i]; + if (entry.timestamp.getTime() >= endTime.getTime()) { + break; + } if (entry.action === GameLogAction.SAVE_GAME) { lastSaveTime = entry.timestamp.getTime(); @@ -342,11 +340,33 @@ export function calculateCurrentTurnDuration(logEntries: ILogEntry[], currentTim } } - // Handle case where the game is currently paused + // Handle case where the end time is during a pause if (pauseStartTime !== null) { - totalPausedTime += currentTime.getTime() - pauseStartTime; + totalPausedTime += endTime.getTime() - pauseStartTime; } + return totalPausedTime; +} + +/** + * Calculates the duration of the current turn from the last NEXT_TURN or START_GAME up to the current time. + * Subtracts the time between any SAVE_GAME and the immediately following LOAD_GAME within the turn. + * @param logEntries - The game log entries. + * @param currentTime - The current time. + * @returns The duration of the current turn in milliseconds. + */ +export function calculateCurrentTurnDuration(logEntries: ILogEntry[], currentTime: Date): number { + if (logEntries.length === 0) { + return 0; + } + const currentTurn = logEntries[logEntries.length - 1].turn; + const turnStartEntry = getTurnStartEntry(logEntries, currentTurn); + + const totalPausedTime = calculatePausedTime( + logEntries, + logEntries.indexOf(turnStartEntry), + currentTime + ); const currentTurnDuration = currentTime.getTime() - turnStartEntry.timestamp.getTime(); return currentTurnDuration - totalPausedTime; } @@ -354,6 +374,8 @@ export function calculateCurrentTurnDuration(logEntries: ILogEntry[], currentTim /** * Calculate the total duration of the game from the start of the game to the current time, subtracting the time between any SAVE_GAME and the immediately following LOAD_GAME within the turn. * @param log - The game log entries + * @param calculateTurnDurations - A function to calculate turn durations from the log entries + * @param calculateCurrentTurnDuration - A function to calculate the duration of the current turn * @returns The total duration of the game in milliseconds */ export function calculateGameDuration( @@ -393,34 +415,7 @@ export function calculateDurationUpToEvent(logEntries: ILogEntry[], eventTime: D return 0; } - let totalPausedTime = 0; - let lastSaveTime: number | null = null; - let pauseStartTime: number | null = null; - - for (let i = 0; i < logEntries.length; i++) { - const entry = logEntries[i]; - if (entry.timestamp.getTime() >= eventTime.getTime()) { - break; - } - - if (entry.action === GameLogAction.SAVE_GAME) { - lastSaveTime = entry.timestamp.getTime(); - } else if (entry.action === GameLogAction.LOAD_GAME && lastSaveTime !== null) { - totalPausedTime += entry.timestamp.getTime() - lastSaveTime; - lastSaveTime = null; - } else if (entry.action === GameLogAction.PAUSE) { - pauseStartTime = entry.timestamp.getTime(); - } else if (entry.action === GameLogAction.UNPAUSE && pauseStartTime !== null) { - totalPausedTime += entry.timestamp.getTime() - pauseStartTime; - pauseStartTime = null; - } - } - - // Handle case where the event time is during a pause - if (pauseStartTime !== null) { - totalPausedTime += eventTime.getTime() - pauseStartTime; - } - + const totalPausedTime = calculatePausedTime(logEntries, 0, eventTime); const eventDuration = eventTime.getTime() - startGameTime; return Math.max(0, eventDuration - totalPausedTime); } @@ -903,6 +898,7 @@ export function prepareGroupedActionTriggers( * Apply a log action to the game. * @param game - The current game state * @param actionDate - The date of the log action + * @param applyLogAction - A function to apply a log action to the game * @returns The updated game state */ export function applyPendingGroupedActions( @@ -944,6 +940,7 @@ export function applyPendingGroupedActions( /** * Get the signed count for a log entry. * @param log - The log entry + * @param defaultValue - The default value to use if the count is not provided * @returns The signed count for the log entry, negative for removal actions and positive for addition actions. */ export function getSignedCount(log: ILogEntry, defaultValue = 0): number { @@ -1012,7 +1009,7 @@ export function getGameEndTime(game: IGame): Date { /** * Get the start time of a turn. - * @param game - The game object + * @param logEntries - The log entries * @param turn - The turn number * @returns The log entry marking the beginning of the turn */ @@ -1067,7 +1064,7 @@ export function getTurnEndTime(game: IGame, turn: number): Date { // a turn ends with either a NEXT_TURN or END_GAME action // a NEXT_TURN will have the next higher turn number than the current turn // an END_GAME will have the same turn number as the current turn - let nextTurnLog = undefined; + let nextTurnLog; try { nextTurnLog = getTurnStartEntry(game.log, turn + 1); } catch { @@ -1089,6 +1086,7 @@ export function getTurnEndTime(game: IGame, turn: number): Date { /** * Get the adjustments made during the current turn * @param game - The game object + * @param turn - The turn number (optional) * @returns An array of turn adjustments */ export function getTurnAdjustments(game: IGame, turn?: number): Array { @@ -1185,8 +1183,10 @@ export function getAverageActionsPerTurn(game: IGame): number { /** * Get the next turn number for a given player (when it will be their turn) - * @param game - * @param playerIndex + * @param game - The game object + * @param playerIndex - The index of the player + * @param skipCurrentTurn - Whether to skip the current turn + * @returns The next turn number for the player */ export function getPlayerNextTurnCount( game: IGame, diff --git a/yarn.lock b/yarn.lock index ad5fd36..a4aab3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1202,11 +1202,16 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0": +"@eslint-community/regexpp@^4.10.0": version "4.11.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.1.tgz#a547badfc719eb3e5f4b556325e542fbe9d7a18f" integrity sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q== +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + "@eslint/compat@^1.1.1": version "1.2.0" resolved "https://registry.yarnpkg.com/@eslint/compat/-/compat-1.2.0.tgz#8d36b8c0e1e9e91068a1df8938977a9e4535d83c" @@ -1221,10 +1226,10 @@ debug "^4.3.1" minimatch "^3.1.2" -"@eslint/core@^0.6.0": - version "0.6.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.6.0.tgz#9930b5ba24c406d67a1760e94cdbac616a6eb674" - integrity sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg== +"@eslint/core@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.7.0.tgz#a1bb4b6a4e742a5ff1894b7ee76fbf884ec72bd3" + integrity sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw== "@eslint/eslintrc@^3.1.0": version "3.1.0" @@ -1241,7 +1246,12 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.12.0", "@eslint/js@^9.8.0": +"@eslint/js@9.14.0": + version "9.14.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.14.0.tgz#2347a871042ebd11a00fd8c2d3d56a265ee6857e" + integrity sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg== + +"@eslint/js@^9.8.0": version "9.12.0" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.12.0.tgz#69ca3ca9fab9a808ec6d67b8f6edb156cbac91e1" integrity sha512-eohesHH8WFRUprDNyEREgqP6beG6htMeUYeCpkEgBCieCMme5r9zFWjzAJp//9S+Kub4rqE+jXe9Cp1a7IYIIA== @@ -1301,17 +1311,17 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@humanfs/core@^0.19.0": - version "0.19.0" - resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.0.tgz#08db7a8c73bb07673d9ebd925f2dad746411fcec" - integrity sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw== +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== -"@humanfs/node@^0.16.5": - version "0.16.5" - resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.5.tgz#a9febb7e7ad2aff65890fdc630938f8d20aa84ba" - integrity sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg== +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== dependencies: - "@humanfs/core" "^0.19.0" + "@humanfs/core" "^0.19.1" "@humanwhocodes/retry" "^0.3.0" "@humanwhocodes/module-importer@^1.0.1": @@ -1319,11 +1329,16 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/retry@^0.3.0", "@humanwhocodes/retry@^0.3.1": +"@humanwhocodes/retry@^0.3.0": version "0.3.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== +"@humanwhocodes/retry@^0.4.0": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== + "@icons/material@^0.2.4": version "0.2.4" resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.2.4.tgz#e90c9f71768b3736e76d7dd6783fc6c2afa88bc8" @@ -3537,6 +3552,11 @@ acorn@^8.1.0, acorn@^8.11.0, acorn@^8.12.0, acorn@^8.4.1, acorn@^8.5.0, acorn@^8 resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + address@^1.0.1: version "1.2.2" resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e" @@ -5553,10 +5573,10 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.1.0.tgz#70214a174d4cbffbc3e8a26911d8bf51b9ae9d30" - integrity sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw== +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -5571,21 +5591,26 @@ eslint-visitor-keys@^4.1.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz#1f785cc5e81eb7534523d85922248232077d2f8c" integrity sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg== -eslint@^9.8.0: - version "9.12.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.12.0.tgz#54fcba2876c90528396da0fa44b6446329031e86" - integrity sha512-UVIOlTEWxwIopRL1wgSQYdnVDcEvs2wyaO6DGo5mXqe3r16IoCNWkR29iHhyaP4cICWjbgbmFUGAhh0GJRuGZw== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.14.0: + version "9.14.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.14.0.tgz#534180a97c00af08bcf2b60b0ebf0c4d6c1b2c95" + integrity sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.11.0" + "@eslint-community/regexpp" "^4.12.1" "@eslint/config-array" "^0.18.0" - "@eslint/core" "^0.6.0" + "@eslint/core" "^0.7.0" "@eslint/eslintrc" "^3.1.0" - "@eslint/js" "9.12.0" + "@eslint/js" "9.14.0" "@eslint/plugin-kit" "^0.2.0" - "@humanfs/node" "^0.16.5" + "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.3.1" + "@humanwhocodes/retry" "^0.4.0" "@types/estree" "^1.0.6" "@types/json-schema" "^7.0.15" ajv "^6.12.4" @@ -5593,9 +5618,9 @@ eslint@^9.8.0: cross-spawn "^7.0.2" debug "^4.3.2" escape-string-regexp "^4.0.0" - eslint-scope "^8.1.0" - eslint-visitor-keys "^4.1.0" - espree "^10.2.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -5612,7 +5637,7 @@ eslint@^9.8.0: optionator "^0.9.3" text-table "^0.2.0" -espree@^10.0.1, espree@^10.2.0: +espree@^10.0.1: version "10.2.0" resolved "https://registry.yarnpkg.com/espree/-/espree-10.2.0.tgz#f4bcead9e05b0615c968e85f83816bc386a45df6" integrity sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g== @@ -5621,6 +5646,15 @@ espree@^10.0.1, espree@^10.2.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^4.1.0" +espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + espree@^9.0.0: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"